解决 C++ 构造函数和调用歧义
Resolving C++ constructor & call ambiguity
我有一个不错的小万事通 Array 类型,可以满足我现在的所有需求。
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 = { ... }
,并分配和 return {...}
初始化列表。因为 none 的构造函数是显式的,非常方便,但这也是我现在卡住的原因。
我希望能够向数组中添加任何内容 "reasonable"。我现在的主要用例是:
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<>
的东西就是专门为了能够把所有的曲线都看成是一样的,但不是一样的:不同阶数的曲线类型,一切都很好"statically typed",所以所有我在 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
.
时,您才需要此构造函数 selected
可能有一种方法可以想出一些复杂的东西并将其塞入一个 std::enable_if
,然后将其推入此模板。但是为了清楚和简单起见,我会使用一个助手 class:
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 用于 select 此构造函数,类似于您的方式在另一个构造函数中已经使用 std::enable_if
。
这应该可以解决所有歧义。然后,在这个助手 class 的帮助下,应该仅使用至少一个不是 Array
的模板参数来选择此构造函数。当它是一个 Array
时,这个构造函数将无法解析,那个 case 将转到另一个构造函数。
我还建议在模板中使用通用引用,而不是 const T &
s。
我有一个不错的小万事通 Array 类型,可以满足我现在的所有需求。
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 = { ... }
,并分配和 return {...}
初始化列表。因为 none 的构造函数是显式的,非常方便,但这也是我现在卡住的原因。
我希望能够向数组中添加任何内容 "reasonable"。我现在的主要用例是:
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<>
的东西就是专门为了能够把所有的曲线都看成是一样的,但不是一样的:不同阶数的曲线类型,一切都很好"statically typed",所以所有我在 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
,然后将其推入此模板。但是为了清楚和简单起见,我会使用一个助手 class:
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 用于 select 此构造函数,类似于您的方式在另一个构造函数中已经使用 std::enable_if
。
这应该可以解决所有歧义。然后,在这个助手 class 的帮助下,应该仅使用至少一个不是 Array
的模板参数来选择此构造函数。当它是一个 Array
时,这个构造函数将无法解析,那个 case 将转到另一个构造函数。
我还建议在模板中使用通用引用,而不是 const T &
s。