【问题标题】:Template template argument deduction failure with GCC (works with MSVC)GCC 的模板模板参数推导失败(适用于 MSVC)
【发布时间】:2018-06-24 09:56:02
【问题描述】:

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

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>());

                                 ^

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

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

顺便说一下,明确指定模板参数并没有帮助。

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

【问题讨论】:

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


    【解决方案1】:

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

    您声明的模板模板参数SupersetType 仅包含一个模板参数,但template template argument std::vector&lt;std::string&gt; 有两个,即std::string 和默认模板参数std::allocator&lt;string&gt;。在 C++17 之前,它们不匹配并导致错误(然后您必须使它们匹配才能解决问题),因为 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
    

    【讨论】:

    • 那么,这一变化允许编译器将B 视为只有一个模板参数(因为另一个模板参数有默认值)?
    • @VioletGiraffe 是的。
    【解决方案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 不只接受一个类型,而是两个;第二个是具有默认值的分配器。

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

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

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

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

    【讨论】:

    • 呃,分配器又来了。我一直在忘记它。谢谢。
    【解决方案3】:

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

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

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

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

    【讨论】:

    • 好点,但我希望它更通用,并支持只有 begin() / end() 且不一定定义 value_type 的容器。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-11
    • 1970-01-01
    • 2012-12-06
    • 1970-01-01
    • 1970-01-01
    • 2020-06-05
    • 1970-01-01
    相关资源
    最近更新 更多