变体模板,呼叫

时间:2018-01-23 15:36:09

标签: c++ c++11 templates variadic-templates template-deduction

我正在尝试使用可变参数模板来重构我的一些代码,但编译器“没有匹配的调用函数”错误。下面是一个简化版本(它可能对功能没有意义,但是重现错误的一个例子):

// base case
void testFunc(int i) { std::cout << i << std::endl; }

template <class T, class... Args> void testFunc(int i) {
  T t = 0;
  std::cout << t << std::endl;
  testFunc<Args...>(i);
}

int main() {
  testFunc<int, long, float>(1);
  return 0;
}

错误消息:

main.cpp:9:3: error: no matching function for call to 'testFunc'
  testFunc<Args...>(i);
  ^~~~~~~~~~~~~~~~~
main.cpp:9:3: note: in instantiation of function template specialization 'testFunc<float>' requested here
main.cpp:9:3: note: in instantiation of function template specialization 'testFunc<long, float>' requested here
main.cpp:13:3: note: in instantiation of function template specialization 'testFunc<int, long, float>' requested here
  testFunc<int, long, float>(1);
  ^
main.cpp:6:40: note: candidate template ignored: couldn't infer template argument 'T'
template <class T, class... Args> void testFunc(int i) {
                                       ^
1 error generated.

看起来模板参数的展开工作正常,并在基本情况下停止。但我已经定义了基本情况。为什么没有匹配功能?

3 个答案:

答案 0 :(得分:3)

问题在于调用

testFunc<Args...>(i);

您调用testFunc()的模板版本,而不是基本案例版本。

如果Args...为空,则表示没有可用的模板版本。

要解决此问题...如果您可以使用C ++ 17,则可以按照YSC的建议使用if constexpr

对于C ++ 11和C ++ 14,我建议使用struct的模板部分特化。

以下是一个完整的工作示例

#include <iostream>

// base case  
template <typename...>
struct foo
 { static void bar (int i) { std::cout << i << std::endl; } };

// recursive case
template <typename T, typename ... Args>
struct foo<T, Args...>
 {
   static void bar (int i)
    {
      std::cout << T{} << std::endl;

      foo<Args...>::bar(i);
    }
 };

int main()
 {
   foo<int, long, float>::bar(1);
 }

答案 1 :(得分:2)

对于单一类型,您的函数定义不明确,因为它尝试调用与b不同的void testFunct<>(int)

您可以在使用C ++ 17 void testFunc(int i)进行递归之前测试参数包的大小:

constexp if

答案 2 :(得分:1)

如前所述,问题来自testFunc<int>(int i)试图调用testFunct<>(int t)

的事实

但是在C ++中,testFunct<>(int t)testFunct(int t)

不同

另请注意,在C ++中,您可以部分专门化函数,例如here所述。

接近您的方法的一个解决方案是定义

// Stop recursion
template <class T>
void testFunc(int i)
{
  T t = 0;
  std::cout << t << " " << typeid(T).name() << std::endl;
}

并且由于SFINAE

而避免含糊不清的定义
// instantiated only when Args... is not "empty"
template <class T, class... Args>
typename std::enable_if<sizeof...(Args)>::type testFunc(int i)
{
  T t = 0;
  std::cout << t << " " << typeid(T).name() << std::endl;
  testFunc<Args...>(i);
}

这是完全合法的C ++ 11,它紧跟你的初步猜测

完整运行代码,使用g++ -std=c++11 testVariadic.cpp; ./a.out

进行编译
#include <iostream>
#include <type_traits>

template <class T>
void testFunc(int i)
{
  T t = 0;
  std::cout << t << " " << typeid(T).name() << std::endl;
}

template <class T, class... Args>
typename std::enable_if<sizeof...(Args)>::type testFunc(int i)
{
  T t = 0;
  std::cout << t << " " << typeid(T).name() << std::endl;
  testFunc<Args...>(i);
}

int main()
{
  testFunc<int, long, float>(1);
  return 0;
}

<强>输出:

0 i
0 l
0 f