我有一个漂亮的小万能数组类型,可以满足我到目前为止的所有需求。
template <typename T>
class Array
{
...
public:
int Data; // custom value
virtual void InitData() { Data = 0; }
Array(const Array& array);
template <typename U, typename = std::enable_if<std::is_same<U, T>::value, U>> Array(const Array<U>& array);
template <typename... Ts> Array(const Ts&... items);
void Add(const T& item);
template <typename... Ts> void Add(const T& item, const Ts&... rest);
void Add(const Array& array);
}
template <typename... Ts> Array(const Ts&... items);
让我可以执行 Array<T> array = { ... }
操作,并分配并返回 {...}
初始化列表。因为没有一个构造函数是显式的,所以它非常方便,但这也是我现在陷入困境的原因。
我希望能够向数组添加任何“合理”的内容。我现在的主要用例是:
using Curve = Array<float2>;
class Poly : public Array<float2> { using Array::Array; void InitData() override { Data = 1; } };
class Curve2 : public Array<float2> { using Array::Array; void InitData() override { Data = 2; } };
class Curve3 : public Array<float2> { using Array::Array; void InitData() override { Data = 3; } };
上面的 std::is_same<>
内容专门是为了能够将所有曲线视为相同但不相同:不同程度的曲线类型,并且所有内容都是很好的“静态类型”,所以我在像 DrawCurve(const Curve&)
这样的函数中所做的一切是检查程度然后采取适当的行动。 Curve
是 Array 的一个很好的别名,Curve2 等是程度特化。它工作得非常好。
当我进行曲线构造时,我通常有一个曲线对象,我向其中添加点或曲线段。所以我希望能够做到:
Curve3 curve;
curve.Add(float2()); // ambiguity
curve.Add(Array<float2>());
不幸的是,当我调用 add 时,我在这里遇到了歧义,因为 Add() 将采用 float2
或 Array<float2>
,这工作正常,但 Array 有隐式构造函数 template <typename... Ts> Array(const Ts&...)
,它可以采用 float2
作为参数。所以歧义在于
Array::Add(float2()); // and
Array::Add(Array<float2>(float2()));
我尝试过制作显式接受数组的构造函数,例如
template <typename A, typename = std::enable_if<std::is_same<A, Array>::value, A>>
void Add(const Array& array);
但是后来我收到了从 Curve3 到 float2 等的新转换错误,结果变得一团糟。
我希望模板或其他 C++ 好东西的深处有一个简单的解决方案,这正是我所需要的。 (是的,我知道我可以重命名方法::AddItem() 和::AddArray() ,问题将在一秒钟内结束,但我不想要这个,因为最终我想用 +=
将所有这些加倍然后大多数情况下就使用它。
有什么想法吗?
最佳答案
观察你想要的
template <typename... Ts> Array(const Ts&... items);
仅当参数包至少包含一项且该项的类型不是 Array
模板实例时才使用。如果参数包为空,则这将成为默认构造函数,因此我们单独处理这种情况。如果需要的话,继续显式定义一个默认构造函数,并让它做它需要做的事情;现在我们可以从这个用例中消除这种可能性,并继续前进。
解决了这一问题后,您在这里要做的就是仅当该构造函数有一个参数时才使用它:
template <typename T1, typename... Ts> Array(const T1 &t1, const Ts&... items);
除了它使用的现有参数包之外,您还必须修改此构造函数才能使用显式 t1
。这应该足够简单,但这还不够。还是有歧义。仅当 T1 不是Array
时,您才希望选择此构造函数。
可能有一种方法可以提出一些复杂的东西并将其填充到单个 std::enable_if
中,然后将其插入此模板中。但为了清楚和简单起见,我将使用一个辅助类:
template<typename T> class is_not_array : public std::true_type {};
template<typename T>
class is_not_array<Array<T>> : public std::false_type {};
然后添加一个简单的 std::enable_if
到这个构造函数的模板中,以便仅当它的第一个模板参数不是 Array
时才使用 SFINAE 选择这个构造函数,类似于您如何在另一个构造函数中使用 std::enable_if
。
这应该可以解决所有歧义。然后,在这个辅助类的帮助下,应该仅使用至少一个不是 Array
的模板参数来选择该构造函数。当它是Array
时,此构造函数将不可解析,并且这种情况将转到另一个构造函数。
我还建议在模板中使用通用引用,而不是 const T &
s。
关于c++ - 解决 C++ 构造函数和调用歧义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41762099/