C ++模板部分特化:为什么我不能匹配variadic-template中的最后一个类型?

时间:2017-02-04 16:28:19

标签: c++ c++11 variadic-templates template-meta-programming template-specialization

我尝试编写IsLast类型特征来检查给定类型是否是std::tuple中的最后一个,但下面的代码不能编译。我知道如何绕过它,但我很好奇为什么编译器不喜欢它。我猜我必须有一些关于variadic-template特化的规则,我不知道。

代码位于:https://godbolt.org/g/nXdodx

错误讯息:

error: implicit instantiation of undefined template
 'IsLast<std::tuple<std::__cxx11::basic_string<char>, int>, int>'

专业化声明也有警告:

  

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

#include <tuple>
#include <string>

/////////This works
template<typename TP, typename T>
struct IsFirst;

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

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

////////This doesn't compile
template<typename TP, typename T>
struct IsLast;

template<typename ...U, typename V, typename T>
struct IsLast <std::tuple<U..., V>, T>
{
  enum {value = false};
};

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


int main()
{
  using T = std::tuple<std::string, int>;
  bool v1 = IsFirst<T, std::string>::value;
  bool v2 = IsLast <T, int>::value;
}

4 个答案:

答案 0 :(得分:9)

在类模板中,参数包必须在所有其他模板参数之后,因此不允许这样的事情。

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

在功能模板中,只有在可以推断出包装后,才能在包装后面显示其他模板参数。类模板不允许这样做,因为它们不允许扣除。

答案 1 :(得分:3)

通过复活观察,可变参数模板必须处于最后位置。

但是还有很多其他方法可以获得相同的结果。

显而易见的解决方案是创建递归类型特征,但我向您展示了基于true的解决方案

std::tuple_element

观察此#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 }

一起使用
IsLast

以及其他模板类。

答案 2 :(得分:2)

编辑:感谢波格丹的评论,标准引用现在是正确的!

根据temp.deduct.type#9

  

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

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

所以你的第一个类模板部分特化的例子是正确的:

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

因为V...tuple的最后一个参数。

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

因为V...不是tuple的最后一个参数。

答案 3 :(得分:0)

有一个更好的解决方案:

#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");

}