【问题标题】:Substitution failure for template template argument模板模板参数替换失败
【发布时间】:2019-07-31 15:42:39
【问题描述】:

我想要一个辅助函数来为我实例化一个类。目前它无法在 clang 中编译(尽管它在 gcc 中编译工作),但我也需要它在 clang 中工作。目前我正在使用clang version 6.0.0-1ubuntu2

我不确定它为什么会失败,因为 gcc 能够检测到类型。我尝试从this post 做一些事情并玩了一段时间,但我一直撞到墙上。 MCVE 可用,或者您可以在coliru here 上试用:

#include <vector>

using namespace std;

template <typename T, template <typename> typename Container>
struct SomeClass {
    SomeClass(const Container<T>& c) {
    }
};

template <typename T, template <typename> typename C>
inline auto make_some_class(const C<T>& container) {
    return SomeClass<T, C>(container);
}

int main() {
    vector<int> ints;

    auto stuff = make_some_class(ints);  
}

main.cpp:19:18: 错误:没有匹配的函数调用“make_some_class”

   auto stuff = make_some_class(ints);  

                ^~~~~~~~~~~~~~~

main.cpp:12:13: 注意:候选模板被忽略:替换失败 [with T = int]:模板模板参数的模板参数与其对应的模板模板参数不同

inline auto make_some_class(const C<T>& container) {

            ^

生成 1 个错误。

【问题讨论】:

  • 这看起来像是不可推断的上下文,即使我们暂时忽略std::vector 有两个模板参数,而不是一个,并且一开始就不会匹配template&lt;typename&gt; typename Container 的基本事实。跨度>
  • 我希望您使用的是 Clang,如果您添加编译标志 -frelaxed-template-template-args,它将起作用。
  • @SamVarshavchik 这不是不可演绎的上下文。

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


【解决方案1】:

建议:试试

#include <vector>

template <template <typename...> typename Container, typename ... Ts>
struct SomeClass {
    SomeClass(const Container<Ts...>& c) {
    }
};

template <template <typename...> typename C, typename ... Ts>
inline auto make_some_class(const C<Ts...>& container) {
    return SomeClass<C, Ts...>(container);
}

int main() {
    std::vector<int> ints;

    auto stuff = make_some_class(ints);  
}

我的意思是...我想问题是std::vector 不是接收 one 类型模板参数的容器;它是一个接收 两个 类型模板参数的容器(第二个具有默认类型:std::allocator&lt;T&gt; 其中T 是第一个)。

所以建议是:让SomeClass 更灵活,并且能够接收带有参数模板类型可变列表的容器

template <typename...> typename Container

对应的模板类型列表

typename ... Ts

如果你想要一个可变参数列表(Ts...),你需要它在最后一个位置,所以你必须切换ContainerT(现在是Ts...)的位置:在Container之前和之后可变参数列表Ts...

template <template <typename...> typename Container, typename ... Ts>
struct SomeClass {
    SomeClass(const Container<Ts...>& c) {
    }
};

不是严格要求,但为了统一起见,我建议以相同的方式重写make_some_class()(显然,在模板参数列表中将C 传递到Ts... 之前)。

template <template <typename...> typename C, typename ... Ts>
inline auto make_some_class(const C<Ts...>& container) {
    return SomeClass<C, Ts...>(container);
}

【讨论】:

    【解决方案2】:

    让我们将其简化为:

    template <template <typename> class C, typename T>
    void foo(C<T> const&) { }
    
    std::vector<int> v;
    foo(v);
    

    你会发现 gcc 可以编译,而 clang 不能。两者的原因都很有趣。

    首先,回忆一下std::vector是一个类模板,它接受两个模板参数:

    template <typename T, typename Alloc = std::allocator<T>>
    class vector { ... };
    

    为什么 gcc 认为匹配 template &lt;typename&gt; class C - 一个只有 一个 模板参数的模板?因为P0522R0 导致规则发生了变化。这是有道理的——我们可以使用vector,就好像它在普通代码中只有一个类型参数,所以它应该能够匹配这样一个模板参数。

    现在,为什么 clang 认为 vector 匹配?因为他们明确选择不采用这条规则。来自their docs

    (10):尽管是缺陷报告的解决方案,但在所有语言版本中默认禁用此功能,并且可以在 Clang 4 及更高版本中使用标志 -frelaxed-template-template-args 显式启用。对标准的更改缺少模板偏序的相应更改,导致合理且先前有效的代码出现歧义错误。预计此问题将很快得到纠正。

    也就是说,它可能是break code


    当然,您可能只是想知道如何解决它。只需更改模板模板参数C的声明即可:

    template <template <typename...> class C, typename T>
    void foo(C<T> const&) { }
    
    std::vector<int> v;
    foo(v); // ok in both gcc and clang
    

    【讨论】:

      【解决方案3】:

      正如评论和 max66 中已经建议的那样,std::vector 有两个模板参数,value_typeallocator_type。 在 C++17 之前,我们必须将这两个参数显式写为模板模板参数,如下所示,这也适用于 C++17:

      DEMO

      template <typename T, template <typename V, typename Allocator = std::allocator<T>> typename Container>
      struct SomeClass {
          SomeClass(const Container<T>& c) {
          }
      };
      
      template <typename T, template <typename V, typename Allocator = std::allocator<T>> typename C>
      inline auto make_some_class(const C<T>& container) {
          return SomeClass<T, C>(container);
      }
      

      顺便说一句,由于 C++17,"Matching template template parameters to compatible arguments" [P0522R0] 被接受,如果您使用 C++17,您的代码是正确的。 但是Clang仍然默认禁用这个新特性,编译标志-frelaxed-template-template-args启用这个特性。

      DEMO

      【讨论】:

      • 默认情况下clang禁用此功能的原因将改善此答案。
      猜你喜欢
      • 2013-06-20
      • 1970-01-01
      • 2018-04-28
      • 2019-09-11
      • 2014-09-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多