C++ C++;模板部分专门化:为什么我不能匹配可变模板中的最后一种类型?

C++ C++;模板部分专门化:为什么我不能匹配可变模板中的最后一种类型?,c++,c++11,variadic-templates,template-meta-programming,template-specialization,C++,C++11,Variadic Templates,Template Meta Programming,Template Specialization,我试图编写一个IsLast类型traits来检查给定类型是否是std::tuple中的最后一个类型,但下面的代码无法编译。我知道如何绕过它,但我很好奇为什么编译器不喜欢它。我想一定有一些关于变量模板专门化的规则,我不知道 代码位于: 错误消息: error: implicit instantiation of undefined template 'IsLast<std::tuple<std::__cxx11::basic_string<char>, int>,

我试图编写一个
IsLast
类型traits来检查给定类型是否是
std::tuple
中的最后一个类型,但下面的代码无法编译。我知道如何绕过它,但我很好奇为什么编译器不喜欢它。我想一定有一些关于变量模板专门化的规则,我不知道

代码位于:

错误消息:

error: implicit instantiation of undefined template
 'IsLast<std::tuple<std::__cxx11::basic_string<char>, int>, int>'
错误:未定义模板的隐式实例化
“IsLast”
专门化声明中还有一个警告:

警告:类模板部分专门化包含无法推导的模板参数;这种局部专门化永远不会被使用

#包括
#包括
/////////这很有效
模板
结构优先;
模板
结构是第一位的
{
枚举{value=false};
};
模板
结构是第一位的
{
枚举{value=true};
};
////////这是不可编译的
模板
结构IsLast;
模板
结构IsLast
{
枚举{value=false};
};
模板
结构IsLast
{
枚举{value=true};
};
int main()
{
使用T=std::tuple;
bool v1=IsFirst::value;
bool v2=IsLast::value;
}

在类模板中,参数包必须位于所有其他模板参数之后,因此不允许出现类似的情况

template<typename U, typename ...V, typename T>
struct IsFirst <std::tuple<U, V...>, T>
{
  enum {value = false};
};
模板
结构是第一位的
{
枚举{value=false};
};

在函数模板中,只有在可以推导出其他模板参数的情况下,才能在包之后有其他模板参数。类模板不允许这样做,因为它们不允许扣减。

有一个更好的解决方案:

#include <tuple>
#include <string>
#include <type_traits>

int main()
{
    using T = std::tuple<std::string, int>;

    constexpr size_t size = std::tuple_size<T>::value;

    typedef decltype(std::get<size - 1>(std::declval<T>())) LastType;

    static_assert(std::is_same<std::decay_t<LastType>, int>::value, "no");

}
#包括
#包括
#包括
int main()
{
使用T=std::tuple;
constexpr size\u t size=std::tuple\u size::value;
typedef decltype(std::get(std::declval()))LastType;
静态断言(std::is_same::value,“no”);
}

根据复活观察,可变模板必须位于最后位置

但是有很多其他的方法可以得到同样的结果

显而易见的解决方案是创建一个递归类型traits,但我向您展示了一个基于
std::tuple\u元素的解决方案

#include <tuple>
#include <iostream>
#include <type_traits>

template <typename C, typename T>
struct IsLast;

template <template <typename ...> class C, typename T, typename ... Ts>
struct IsLast<C<Ts...>, T>
 {
   using Tl
    = typename std::tuple_element<sizeof...(Ts)-1U, std::tuple<Ts...>>::type;

   static constexpr bool value { std::is_same<Tl, T>::value };
 };

int main ()
 {
   using T = std::tuple<std::string, int>;

   std::cout << IsLast<T, int>::value << std::endl;  // print 1
   std::cout << IsLast<T, long>::value << std::endl; // print 0
 }

还有其他模板类。

编辑:多亏了Bogdan的评论,标准引用现在是正确的

根据:

如果p的模板参数列表包含不是最后一个模板参数的包扩展,则整个模板参数列表是非推断上下文

请注意,只有在模板的参数列表中,包必须是最后一个。模板参数包不需要是模板参数列表中的最后一个,这可能是模板类部分专门化或模板函数的情况

因此,类模板部分专门化的第一个示例是正确的:

template<typename U, typename ...V>
struct IsFirst <std::tuple<U, V...>, U>
{
   enum {value = true};
}

因为
V..
不是
tuple

的最后一个参数,我相信变量模板必须排在最后。您使用的编译器、版本和编译标志是什么?确切的错误信息是什么?您的意思是将“typename…U”移到下面的最后一个位置吗?它仍然没有编译。模板结构IsLast{enum{value=false};}@BasileStarynkevitch:这是叮当声3.9.1@新王:你应该编辑你的问题,并在那里显示准确的错误信息。IsFirst确实有效。是IsLast没有编译。我明白了。我误解了你的答案。这是有道理的。谢谢你的回答,你错了。只有在参数列表中,参数包必须是最后一个。读我的答案。我肯定会相信我错了。我还没有看到你的答案,我已经加了。模板参数位于最后位置的限制仅适用于参数列表中的模板参数包扩展。对于模板类声明,参数列表是参数列表(这是标准§14.5.5中写入的文字),这就是为什么模板参数包必须是类模板声明的最后一个参数。该段适用于部分专门化本身的模板参数列表,不向嵌套模板参数列表中指定的参数。在这种情况下适用的是:“如果P的模板参数列表包含不是最后一个模板参数的包扩展,则整个模板参数列表是一个非推断上下文。”。这与问题中提到的编译器消息一致。
template<typename ...V,typename U>
struct IsFirst <std::tuple<V..., U>, U>
{
   enum {value = true};
};
template<typename U, typename ...V>
struct IsFirst <std::tuple<U, V...>, U>
{
   enum {value = true};
}
template<typename ...V,typename U>
struct IsFirst <std::tuple<V..., U>, U>
{
   enum {value = true};
};