0

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

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;
}
4

1 回答 1

4
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。

于 2012-10-23T17:11:26.320 回答