精确模板参数上的 C++ 重载冲突
C++ overloading conflict on exact template argument
下面定义的bar
允许两种初始化(bar
的模板总是有几个int
在使用)
template<class C>
inline void store(C dst) {}
template<class T, class C, class... E>
inline void store(C dst, T val, E... etc) {
*dst = val;
store(++dst, etc...);
}
template<class... T>
class bar {
static const int n = sizeof...(T);
int data[n];
public:
bar(int x) {for(int i=0; i<n; i++) data[i]=x;}
bar(T... x) {store(data,x...);}
};
看起来不错;然而,如果模板恰好是 one int
,则此代码对编译器来说是有歧义的(尽管两者的理解具有相同的含义):
bar<int> s(3);
避免这种情况的唯一方法是专门化 int
一种情况吗? (无论如何,这会使代码更加复杂)
如何转换模板中的第二个构造函数并且 SFINAE 仅在 T...
不是 int
时启用它?
我的意思是
template <std::size_t N = sizeof...(T),
typename = std::enable_if_t<
(N != 1u)
|| (false == std::is_same<std::tuple<int, T...>,
std::tuple<T..., int>>{})>>
bar(T... x) {store(data,x...);}
显然,如果只能使用 C++11,则必须使用 typename std::enable_if<>::type
而不是 std::enable_if_t<>
。
如果您可以使用 C++17,则可以使用模板折叠来检查 T...
不是 int
,正如 Jans 所建议的那样。
当只有一个参数并且是 int
.
时,您可以禁用可变参数构造函数
如果你有 c++17,你可以这样做
template <
std::size_t N = sizeof...(T),
std::enable_if_t<(N != 1 || !(std::is_same_v<T, int> && ...)), bool> = true>
bar(T... x) {store(data,x...);}
否则,你可以同意:
template <bool... Pred>
struct all_dummy;
template <bool... Preds>
using all = std::is_same<all_dummy<Preds...>, all_dummy<((void)Preds, true)...>>;
template <
std::size_t N = sizeof...(T),
std::enable_if_t<(N != 1 || !all<std::is_same<T, int>::value...>::value), bool> = true
>
bar(T... x) {store(data,x...);}
Is the only way to avoid this to specialization the one int case?
没有,如 SFINAE 的其他答案所示。
C++20 甚至允许更好的语法 requires
:
template <class... Ts>
class bar {
static const int n = sizeof...(Ts);
int data[n];
public:
bar(int x) { std::fill(std::begin(data), std::end(data), x);}
bar(Ts... xs) requires (n != 1) : data{xs...} {}
};
(That anyway makes the code more complex)
与SFINAE相比,不太同意,专业化,可能是:
template <class... Ts>
class bar_impl
{
protected:
static const int n = sizeof...(Ts);
int data[n];
public:
bar(Ts... xs) : data{xs...} {}
};
template <>
class bar_impl<int> {
static const int n = 1;
int data[n];
};
template <class... Ts>
class bar : bar_impl<Ts...> {
public:
using bar_impl<Ts...>::bar_impl;
bar(int x) { std::fill(std::begin(data), std::end(data), x);}
// ...
};
bar
允许两种初始化(bar
的模板总是有几个int
在使用)
template<class C>
inline void store(C dst) {}
template<class T, class C, class... E>
inline void store(C dst, T val, E... etc) {
*dst = val;
store(++dst, etc...);
}
template<class... T>
class bar {
static const int n = sizeof...(T);
int data[n];
public:
bar(int x) {for(int i=0; i<n; i++) data[i]=x;}
bar(T... x) {store(data,x...);}
};
看起来不错;然而,如果模板恰好是 one int
,则此代码对编译器来说是有歧义的(尽管两者的理解具有相同的含义):
bar<int> s(3);
避免这种情况的唯一方法是专门化 int
一种情况吗? (无论如何,这会使代码更加复杂)
如何转换模板中的第二个构造函数并且 SFINAE 仅在 T...
不是 int
时启用它?
我的意思是
template <std::size_t N = sizeof...(T),
typename = std::enable_if_t<
(N != 1u)
|| (false == std::is_same<std::tuple<int, T...>,
std::tuple<T..., int>>{})>>
bar(T... x) {store(data,x...);}
显然,如果只能使用 C++11,则必须使用 typename std::enable_if<>::type
而不是 std::enable_if_t<>
。
如果您可以使用 C++17,则可以使用模板折叠来检查 T...
不是 int
,正如 Jans 所建议的那样。
当只有一个参数并且是 int
.
如果你有 c++17,你可以这样做
template <
std::size_t N = sizeof...(T),
std::enable_if_t<(N != 1 || !(std::is_same_v<T, int> && ...)), bool> = true>
bar(T... x) {store(data,x...);}
否则,你可以同意:
template <bool... Pred>
struct all_dummy;
template <bool... Preds>
using all = std::is_same<all_dummy<Preds...>, all_dummy<((void)Preds, true)...>>;
template <
std::size_t N = sizeof...(T),
std::enable_if_t<(N != 1 || !all<std::is_same<T, int>::value...>::value), bool> = true
>
bar(T... x) {store(data,x...);}
Is the only way to avoid this to specialization the one int case?
没有,如 SFINAE 的其他答案所示。
C++20 甚至允许更好的语法 requires
:
template <class... Ts>
class bar {
static const int n = sizeof...(Ts);
int data[n];
public:
bar(int x) { std::fill(std::begin(data), std::end(data), x);}
bar(Ts... xs) requires (n != 1) : data{xs...} {}
};
(That anyway makes the code more complex)
与SFINAE相比,不太同意,专业化,可能是:
template <class... Ts>
class bar_impl
{
protected:
static const int n = sizeof...(Ts);
int data[n];
public:
bar(Ts... xs) : data{xs...} {}
};
template <>
class bar_impl<int> {
static const int n = 1;
int data[n];
};
template <class... Ts>
class bar : bar_impl<Ts...> {
public:
using bar_impl<Ts...>::bar_impl;
bar(int x) { std::fill(std::begin(data), std::end(data), x);}
// ...
};