GCC 的模板模板参数推导失败(适用于 MSVC)

Template template argument deduction failure with GCC (works with MSVC)

我有以下相当简单的函数模板:

template <class OrderedSetType, template<class> class SupersetType>
OrderedSetType f(const SupersetType<OrderedSetType>& superset)
{
    return OrderedSetType();
}

它的名字是这样的:

f(std::vector<std::string>());

并且编译器无法推导模板参数。诊断消息不是特别有用:

<source>: In function 'int main()':

<source>:12:33: error: no matching function for call to 'f(std::vector<std::__cxx11::basic_string<char> >)'

     f(std::vector<std::string>());

                                 ^

<source>:5:16: note: candidate: template<class OrderedSetType, template<class> class SupersetType> OrderedSetType f(const SupersetType<OrderedSetType>&)

 OrderedSetType f(const SupersetType<OrderedSetType>& superset)

                ^

<source>:5:16: note:   template argument deduction/substitution failed:

<source>:12:33: note:   template parameters of a template template argument are inconsistent with other deduced template arguments

     f(std::vector<std::string>());

                                 ^

为什么会出现这个错误?在 -std=c++14 的 GCC 7.3 中发生,在 -std=c++17 中不会发生。 C++ 17 标准中的哪些更改允许此代码编译?我可以让它为 C++14 编译吗?

这是现场演示:https://godbolt.org/g/89BTzz

顺便说一句,显式指定模板参数没有帮助。

P. S. 同时MSVC对这段代码没有问题,但是clang 5和6即使在C++17模式下也无法编译。因此,要么 clang 有一个错误并且无法编译符合标准的代码,要么 GCC 有一个错误并成功编译了它不应该的代码(-std=c++17)。

试试

template <template <typename...> class SupersetType,
          typename FirstT, typename ... OthersTs>
FirstT f (SupersetType<FirstT, OthersTs...> const & superset)
 { return FirstT{}; }

template <template <typename...> class SupersetType, typename FirstT>
FirstT f (SupersetType<FirstT> const & superset)
 { return FirstT{}; }

问题是 std::vector 不只接受一种类型,而是接受两种类型;第二个是具有默认值的分配器。

所以你必须考虑这个问题。

显然你可以写 f() 用一个只接受两种类型的模板模板参数

template <template <typename, typename> class SupersetType,
          typename FirstT, typename SecondT>
FirstT f (SupersetType<FirstT, SecondT> const & superset)
 { return FirstT{}; }

但如果您使用接受可变类型列表的模板参数,您将拥有更灵活的 f()(匹配更多容器)

虽然这不能为您的问题提供答案,但它提供了替代方法。

请记住,所有标准容器都有一个名为 value_type 的 public 类型。这意味着您可以轻松跳过模板模板,只使用

之类的东西
template<typename ContainerT>
typename ContainerT::value_type f(ContainerT const& superset)
{
    return typename ContainerT::value_type();
}

只要您的 SupersetType 遵循带有 value_type 成员的标准容器,它就应该可以工作。

Which changes in the C++ 17 standard allowed for this code to compile?

您正在声明模板模板参数 SupersetType 仅包含一个模板参数,但 template template argument std::vector<std::string> has two, i.e. std::string and the default template argument std::allocator<string>. Before C++17 they don't match and leads to error (then you have to make them match to solve the issue), since C++17 (CWG 150) 它是允许的;即默认模板参数允许模板模板参数匹配具有较少模板参数的模板模板参数。

template<class T> class A { /* ... */ };
template<class T, class U = T> class B { /* ... */ };
template <class ...Types> class C { /* ... */ };

template<template<class> class P> class X { /* ... */ };
X<A> xa; // OK
X<B> xb; // OK in C++17 after CWG 150
         // Error earlier: not an exact match
X<C> xc; // OK in C++17 after CWG 150
         // Error earlier: not an exact match