GCC模板模板参数推导失败(与MSVC一起使用)

时间:2018-06-24 09:56:02

标签: c++ c++14 c++17 template-deduction template-templates

我有以下相当简单的功能模板:

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)的代码。

3 个答案:

答案 0 :(得分:2)

尝试

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不只接受一个类型,而是接受两个;第二个是具有默认值的分配器。

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

很明显,您可以使用仅接受两种类型的template-template参数编写f()

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

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

答案 1 :(得分:2)

  

C ++ 17标准中的哪些更改允许此代码编译?

您要声明模板模板参数SupersetType仅包含一个模板参数,但是template template argument std::vector<std::string>有两个,即std::string和默认模板参数{{ 1}}。在C ++ 17之前,它们不匹配并导致错误(然后必须匹配它们才能解决问题),因为C ++ 17(CWG 150)被允许;也就是说,允许模板模板参数使用默认模板参数,以匹配模板模板参数较少的模板模板参数。

std::allocator<string>

答案 2 :(得分:1)

虽然这不能解决您的问题,但提供了替代方法。

请记住,所有标准容器都具有名为value_type的公共类型。这意味着您可以轻松地跳过模板模板,而只拥有类似

的内容
template<typename ContainerT>
typename ContainerT::value_type f(ContainerT const& superset)
{
    return typename ContainerT::value_type();
}

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