【问题标题】:Ambiguity with variadic templates可变参数模板的歧义
【发布时间】:2012-10-23 17:08:53
【问题描述】:

我有一个关于可变参数模板的问题。我有一个类以下列方式使用它们:

template <class... T>
struct A {
  A(B& arg);                   // (1)
  A(typename T::B& args...);   // (2)
};

typename T::B 是某种类型,预期对于参数包中的所有实例都相等。为了便于演示,我将这种类型称为B。该类包含参数包中每个参数的B 实例。第二个构造函数(2) 初始化这些成员。为方便起见,有一个构造函数(1),它只接受一个实例并用相同的实例初始化所有成员。

构造函数的定义对我的问题并不重要,你可以将它们留空。下面提供了一个更完整的示例。

现在的问题是,如果你只用一个参数初始化 A,构造函数就会发生冲突。 g++-4.7 在这里有点糊涂,然后跳了出来,但是仔细看课程后,问题就很明显了。

问题:

  1. 标准对这种情况有什么看法?这是应该/可以由编译器解决的歧义还是我应该避免这种情况?

  2. 避免它的最佳策略是什么?根本没有指定第一个构造函数之类的东西吗?我也可以将第一个构造函数的功能放在静态方法中,但这会使 API 更加不均匀。

感谢您的回答!


完整示例:

struct B {};

struct C
{
  using B = ::B;
};

template <class... T>
struct A
{
  A(B& arg) {}
  A(typename T::B & ... args) {}
};

int main()
{
  A<C> x(B()); // Edit: Should be: A<C> X{B()}; But not related to the problem.
  return 0;
}

【问题讨论】:

  • @KerrekSB 如果椭圆在逗号之后就像它是一个参数一样,它只会是一个可变参数函数,比如A(typename T::B&amp; args, ...);
  • @SethCarnegie:哦,对不起,我看错了代码。没关系。
  • @Markus 好吧,当你A&lt;B&gt; x { B() } 时,你想叫哪一个?
  • @SethCarnegie 实际上,在这种情况下我不会在意,因为两个构造函数都做同样的事情。如果调用更专业的构造函数,即(1),它会感觉更自然。但我认为最好的情况是编译器失败(比g++ 现在更优雅)。
  • stackoverflow.com/questions/11386042/…中的潜在问题相同

标签: c++ c++11 variadic-templates ambiguous


【解决方案1】:
A<C> x(B());

是函数声明,不是对象声明。您需要添加括号或使用大括号:

A<C> x { B() };

A<C> x((B()));

另外,要通过引用将临时B 传递给构造函数,您必须将其设为const

A(const B& arg) { }

然后

A<C> x((B()));

工作正常。

现在要解决歧义问题,您需要这样的东西:

#include <type_traits>
#include <iostream>

struct B {};

struct C
{
  using B = ::B;
};

template <class... T>
struct A
{
  A(const B& arg) {
    std::cout << "one" << std::endl;
  }

  template<bool IsNotOne = sizeof...(T) != 1>
  A(const typename std::enable_if<IsNotOne || !std::is_same<B, T>::value, T>::type::B&... args) {
    std::cout << "multiple" << std::endl;
  }
};

int main()
{
  A<B> x1 { B() };         // prints one
  A<C> x2 { B() };         // prints one
  A<C, C> x3 { B(), B() }; // prints multiple
  A<B, B> x4 { B(), B() }; // prints multiple
  return 0;
}

我们将第二个构造函数设为模板,这样我们就可以依赖 SFINAE。

【讨论】:

  • 我看不出这是如何解决歧义的。除此之外,A&lt;C&gt; x(B()) 只会被视为全局范围内的函数声明?此外,B() 不是类型,因此A&lt;C&gt; X(B()) 不是函数声明。
  • @MarkusMayr​​ 函数声明允许在函数范围内,而不仅仅是全局。此外,B() 是声明参数(函数指针)的有效方式。看this question
  • 好的,谢谢。我会看看它。但问题仍然存在,当然,即使我将那部分代码更改为A&lt;C&gt; X{B()}A&lt;C&gt; X((B()))
  • 很好的答案!我还学到了一些关于 SFINAE 和构造函数的知识。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-12-01
  • 1970-01-01
  • 2013-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-24
相关资源
最近更新 更多