Конфликт перегрузки C ++ для точного аргумента шаблона - PullRequest
0 голосов
/ 06 января 2019

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? (Это в любом случае делает код более сложным)

Ответы [ 3 ]

0 голосов
/ 06 января 2019

Вы можете отключить конструктор переменных, когда есть только один аргумент и он 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...);}
0 голосов
/ 06 января 2019

Это единственный способ избежать специализации в случае int?

Нет, как показано с другим ответом 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...} {}
};

(Это в любом случае делает код более сложным)

Не совсем согласен по сравнению со 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);}
    // ...
};
0 голосов
/ 06 января 2019

Как насчет преобразования второго конструктора в шаблонный, и 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, как предложено Джансом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...