【问题标题】:Function specialization with default arguments can't be called because too few arguments无法调用具有默认参数的函数特化,因为参数太少
【发布时间】:2020-10-05 13:34:27
【问题描述】:

我有this 代码

#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
}

我希望函数say_redsay_my_name 的特化,所以我可以在我的代码中轻松调用它,而无需一直提供模板参数。

say_red(2) 工作正常,但 say_red() 无法编译并显示错误消息 “错误:函数的参数太少”

有没有办法让我的代码在不指定参数的情况下编译?


附带说明:如果这样可以解决问题,我也可以将模板参数转换为函数参数。我的问题是,std::bind 在 C++17 中不是 constexpr,所以这不适合我。
此外,我不想在say_* 的每个声明中指定默认参数。可以想象,这是一个示例,而我的“现实生活”枚举有 8 个变体,所以我想尽可能少写代码。

【问题讨论】:

  • 问题是 say_red 是一个指向带有签名 void(unsigned) 的函数的指针(并且它不带有默认参数),即使编译器可以知道哪个特定函数 @987654332 @ 指向它不能知道每种情况。 (例如,如果constexpr auto say_red = get_function_pointer(); 是通过这种方式获得的)。

标签: c++ c++17 template-specialization specialization


【解决方案1】:

默认参数在调用站点被替换。它们不是函数类型的一部分。因此,您需要“某事”来启用say_red 的默认参数。您可以使用 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
}

如果您不介意在每次调用时添加 (),您可以使用仿函数:

#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
}

如果你不喜欢额外的(),你可以这样做:

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
}

请注意,您也可以使用构造函数代替operator(),但使用operator() 更灵活,因为它允许您返回值。关键是分两步进行特化和重载解析(特化函子,然后用一个或零个参数调用operator())。

【讨论】:

  • 很遗憾,但感谢您的解释。但如前所述,这是我不喜欢的方式,因为我不想为每个枚举变体重复相同的代码。
  • 如果你想要它更短,你可以捏着鼻子,使用一个有据可查的临时预处理宏。
  • @hellow 如果这个问题是一个主要问题,我会将say_my_name 设为operator() 的类型
  • @idclev463035818 既然你已经构造了一个对象,为什么不直接使用构造函数,而不是间接使用operator()template&lt;Color color&gt; struct say_my_name { say_my_name( unsigned times = 1 ) { …
  • constexpr auto say_red = [](auto... args){ say_my_name&lt;Color::Red&gt;(args...); }; 允许不重复默认参数。
猜你喜欢
  • 2020-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-28
相关资源
最近更新 更多