精确模板参数上的 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);}
    // ...
};