对没有参数的可变参数模板函数的不明确调用



运行以下命令时:

template <typename T>
struct CodeByType
{
    static const int32_t Value = 7;
};
template <>
struct CodeByType<int>
{
    static const int32_t Value = 1;
};
template <typename Arg, typename... Args>
int32_t Sum()
{
    // The compiler complains on this line
    return Sum<Arg>() + Sum<Args...>();
}
template <typename Arg>
int32_t Sum()
{
    return CodeByType<Arg>::Value;
}
int main()
{
    auto sum = Sum<int, char, double>();
}

我得到:

错误 C2668 "Sum":对重载函数的不明确调用

有人可以解释为什么以及如何克服它吗?

这看起来非常类似于下面的代码,它确实可以编译,所以我想它与不接受任何实际参数Sum有关。

template <typename T>
T adder(T first) {
    return first;
}
template<typename T, typename... Args>
T adder(T first, Args... rest) {
    return first + adder(rest...);
}
int main()
{
    auto sum = adder(1, 7);
}

如果将代码简化为:

Sum<int>();

您会收到更有用的错误消息:

31 : <source>:31:16: error: call to 'Sum' is ambiguous
    auto sum = Sum<int>();
               ^~~~~~~~
17 : <source>:17:9: note: candidate function [with Arg = int, Args = <>]
int32_t Sum()
        ^
24 : <source>:24:9: note: candidate function [with Arg = int]
int32_t Sum()
        ^
1 error generated.

因此,更清楚的是,第一次重载与Args = <>和第二个重载之间存在过载歧义。两者都是可行的。

人们可能会认为是解决方案的专业化:

template <typename Arg>
int32_t Sum<Arg>()
{
    return CodeByType<Arg>::Value;
}

如果标准允许,这确实可以解决问题。不允许部分功能专用化。

C++17解决方案:

这是最优雅的解决方案:

ConstexPR如果救援:

template <typename Arg, typename... Args>
int32_t Sum()
{
    if constexpr(sizeof...(Args) == 0)
      return CodeByType<Arg>::Value;
    else
      return Sum<Arg>() + Sum<Args...>();
}

C++14

解决方案

我们使用SFINAE来启用/禁用我们想要的功能。请注意,函数定义顺序必须颠倒。

template <typename Arg, typename... Args>
auto Sum() -> std::enable_if_t<(sizeof...(Args) == 0), int32_t>
{
      return CodeByType<Arg>::Value;
}

template <typename Arg, typename... Args>
auto Sum() -> std::enable_if_t<(sizeof...(Args) > 0), int32_t>
{
      return Sum<Arg>() + Sum<Args...>();
}

C++11

解决方案

只需将std::enable_if_t<>替换为typename std::enable_if<>::type

在 c++17 中,它只是

template <typename... Args>
int32_t Sum()
{
    return (CodeByType<Args>::Value + ...); // Fold expression
}

在C++11中,您可以执行以下操作:

template <typename... Args>
int32_t Sum()
{
    int32_t res = 0;
    const int32_t dummy[] = {0, (res += CodeByType<Args>::Value)...};
    static_cast<void>(dummy); silent warning about unused variable
    return res;
}

我对模板机制的记忆很旧,但如果我没记错的话,它们的信息在编译过程中的某个时刻被删除了。

我的猜测是,在第二种情况下,函数的区别不是模板类型的差异,而是参数的差异。

在您的情况下,您没有参数,因此去除模板信息后,两个重载版本是相等的,并且在您调用它时无法区分它们。

最新更新