C++ 为什么递归模板的decltype返回类型失败,而返回类型推断工作正常?

C++ 为什么递归模板的decltype返回类型失败,而返回类型推断工作正常?,c++,c++11,recursion,decltype,return-type-deduction,C++,C++11,Recursion,Decltype,Return Type Deduction,在处理C++11类型集时,我尝试实现此函数(精简到最低限度): 似乎test在这里被认为是充分声明的 我想知道为什么这对decltype变量不起作用?即使我启用了C++14支持 PS:事实证明,我甚至不能真正调用C++14函数,所以可能整个事情都搞砸了…一个问题是,您的第一个测试重载不是函数模板,因此不能使用测试()语法调用 但是,decltype实际上不适用于这样的递归变量函数,因为返回类型是函数声明的一部分,因此在查找名称时不会声明重载 您可以在C++11中通过使用模板类来解决此问题: te

在处理C++11类型集时,我尝试实现此函数(精简到最低限度):

似乎
test
在这里被认为是充分声明的

我想知道为什么这对
decltype
变量不起作用?即使我启用了C++14支持


PS:事实证明,我甚至不能真正调用C++14函数,所以可能整个事情都搞砸了…

一个问题是,您的第一个
测试
重载不是函数模板,因此不能使用
测试()
语法调用

但是,
decltype
实际上不适用于这样的递归变量函数,因为返回类型是函数声明的一部分,因此在查找名称时不会声明重载

您可以在C++11中通过使用模板类来解决此问题:

template <typename... Ts>
struct test;

template <typename T, typename... Ts>
struct test<T,Ts...> {
    static decltype(test<Ts...>::value()) value() { return test<Ts...>::value(); }   
};

template <>
struct test<> {
    static bool value() { return true; }
};
模板
结构测试;
模板
结构测试{
静态decltype(test::value())value(){return test::value();}
};
模板
结构测试{
静态布尔值(){return true;}
};


在C++14中,可以使第一个重载采用单个模板参数,第二个重载采用两个模板参数和一个参数包:

template <typename T>
constexpr auto test() -> bool { return true; }

template <typename T, typename U, typename... Rest>
constexpr auto test()
{
  return test<U,Rest...>();
}
模板
constexpr auto test()->bool{return true;}
模板
constexpr自动测试()
{
返回测试();
}

声明模板函数本身时,尚未声明模板函数本身。因此,它对尾部返回类型decltype不可见

你可以用ADL解决这个问题。如果模板函数从与模板函数相同的名称空间中获取参数,则返回类型的查找将愿意查看模板函数本身。这是因为模板在声明它们的位置之前使用上下文,并在每个参数上使用ADL来查找它们的返回类型签名

template<class...Ts> struct types {};

namespace bob{
  struct mytag{};
  constexpr auto test(mytag, types<>) -> bool{ return true; }

  template <typename T, typename... Rest>
  constexpr auto test(mytag, types<T,Rest...>)
  -> decltype( test( mytag{}, types<Rest...>{} ) )
  {
    return test(mytag{},types<Rest...>{});
  }
}
template<class...Ts>
constexpr auto test()
->decltype( bob::test( bob::mytag{}, types<Ts...>{} ) ){
  return bob::test( bob::mytag{}, types<Ts...>{} );
}
模板结构类型{};
名称空间bob{
结构mytag{};
constexpr自动测试(mytag,类型)->bool{return true;}
模板
constexpr自动测试(mytag,类型)
->decltype(测试(mytag{},类型{}))
{
返回测试(mytag{},类型{});
}
}
模板
constexpr自动测试()
->decltype(bob::test(bob::mytag{},type{})){
返回bob::test(bob::mytag{},类型{});
}
您可能需要
constexpr types(){}和mytag类似


这还修复了
test
在原始代码中是非法的这一事实。根据我的经验,通过传递(标记模板包装)值的类型,函数的性能要好得多。重载更友好。

纯C++14解决方案适合您,还是您更喜欢C++11解决方案?其中一个问题是
test()
test()
不同,因此最后一个递归调用有缺陷。@TartanLlama我有一个基于C++11类型模板的解决方案。我只是在路上无意中发现了这一点,我想知道这里发生了什么。我还对如何在C++11或C++14中“正确”地执行此操作感兴趣。@Holt Ooops,很好的观点。您甚至不需要额外的参数
U
,是吗?@Rumburak是的,否则调用将是不明确的。啊,对。我尝试了一个带有函数参数的版本(
template constexpr auto test(T,Rest…Rest){return test(Rest…;}
)。这在没有U的情况下有效。我想我需要更详细地理解重载解析顺序……倒数第二行不应该是return
test()
?@Rumburak(以及其他可能对此感到疑惑的人)如果没有额外的参数U,调用将是不明确的,因为参数包(Rest)可能是空的(零参数),因此变量函数也将只匹配一个参数的调用。附加参数U确保它只匹配至少有2个参数的调用。
constexpr auto test() -> bool;

template <typename T, typename... Rest>
constexpr auto test()
{
  return test<Rest...>();
}
template <typename... Ts>
struct test;

template <typename T, typename... Ts>
struct test<T,Ts...> {
    static decltype(test<Ts...>::value()) value() { return test<Ts...>::value(); }   
};

template <>
struct test<> {
    static bool value() { return true; }
};
template <typename T>
constexpr auto test() -> bool { return true; }

template <typename T, typename U, typename... Rest>
constexpr auto test()
{
  return test<U,Rest...>();
}
template<class...Ts> struct types {};

namespace bob{
  struct mytag{};
  constexpr auto test(mytag, types<>) -> bool{ return true; }

  template <typename T, typename... Rest>
  constexpr auto test(mytag, types<T,Rest...>)
  -> decltype( test( mytag{}, types<Rest...>{} ) )
  {
    return test(mytag{},types<Rest...>{});
  }
}
template<class...Ts>
constexpr auto test()
->decltype( bob::test( bob::mytag{}, types<Ts...>{} ) ){
  return bob::test( bob::mytag{}, types<Ts...>{} );
}