TransWikia.com

Function specialization with default arguments can't be called because too few arguments

Stack Overflow Asked by hellow on November 27, 2020

I have this code

#include <iostream>
#include <type_traits>

enum class Color: char { Red = 'r', Yellow = 'y', Green = 'g' };

template<Color color>
auto say_my_name(unsigned times = 1) {
    for (unsigned i = 0; i < times; i++) {
        std::cout << static_cast<std::underlying_type_t<Color>>(color) << ' ';
    }
    std::cout << std::endl;
}

constexpr auto say_red = say_my_name<Color::Red>;

int main()
{
    say_my_name<Color::Yellow>(3);
    say_my_name<Color::Yellow>();
    say_red(2);
    say_red(); // too few arguments to function
}

I want the function say_red to be a specialization of say_my_name, so I can easily call it in my code, without giving the template argument all the time.

say_red(2) works fine, but say_red() won’t compile with the error message "error: too few arguments to function"

Is there a way to let let my code compile without specifing an argument?


As a sidenote: I would be possible to convert the template argument to a function argument as well, if that solves the problem. My problem is, that std::bind isn’t constexpr in C++17, so that isn’t going for me.
Also I don’t want to specify the default argument in every declration of say_*. As you can imagine, this is an example, and my "real life" enum has 8 variants, so I want to write as little code as possible.

One Answer

Default arguments are substituted at the call site. They are not part of a functions type. Hence you need "something" to enable the default argument for say_red. You can use a lambda:

#include <iostream>
#include <type_traits>

enum class Color: char { Red = 'r', Yellow = 'y', Green = 'g' };

template<Color color>
auto say_my_name(unsigned times = 1) {
    for (unsigned i = 0; i < times; i++) {
        std::cout << static_cast<std::underlying_type_t<Color>>(color) << ' ';
    }
    std::cout << std::endl;
}

constexpr auto say_red = [](unsigned times = 1){
    say_my_name<Color::Red>(times);
};

int main()
{
    say_my_name<Color::Yellow>(3);
    say_my_name<Color::Yellow>();
    say_red(2);
    say_red(); // too few arguments to function
}

In case you don't mind adding a () on each call you can use a functor:

#include <iostream>
#include <type_traits>

enum class Color: char { Red = 'r', Yellow = 'y', Green = 'g' };

template<Color color>
struct say_my_name {
    void operator()(unsigned times = 1) const {
        for (unsigned i = 0; i < times; i++) {
            std::cout << static_cast<std::underlying_type_t<Color>>(color) << ' ';
        }
        std::cout << std::endl;
    }
};

using say_red = say_my_name<Color::Red>;

int main()
{
    say_my_name<Color::Yellow>()(3);
    say_my_name<Color::Yellow>()();
    say_red()(2);
    say_red()(); // too few arguments to function
}

and if you do not like the additional () you can do this:

using say_red = say_my_name<Color::Red>;
constexpr auto red_say = say_red{};

int main()
{
    red_say(2);
    red_say(); // too few arguments to function
}

Note that you could also use the constructor instead of operator(), but using operator() is more flexible as it allows you to return a value. The crux is just to have the specialization and overload resolution in two steps (specialize the functor then call operator() with one or zero arguments).

Correct answer by idclev 463035818 on November 27, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP