Перегрузка и специализация шаблона для приоритета вызова функции

#c #c 11 #c 14

#c #c 11 #c 14

Вопрос:

Со ссылкой на следующий код:

Я пытаюсь условно скомпилировать кучу функций, а затем «упорядочить» их с помощью prioirty_tag class . Мой вопрос в том, что если я заменю enable_if_t<is_nothrow_move_constructible<U>{}>* = nullptr> на enable_if_t<is_nothrow_move_constructible<U>{}>> , результат будет неправильным (по умолчанию используется первая функция).

Что именно там происходит? почему добавление * = nullptr заставляет это работать?

 #include <iostream>
#include <type_traits>

using namespace std;

template <size_t T>
struct priority_tag: priority_tag<T-1> {};
template <>
struct priority_tag<0> {};

template <typename T>
struct my_vec
{   
        template <typename U = T, typename = void>
        void realloc_impl(priority_tag<0> pr)
        {
            cout << "Move throw constructn";
        };

        //template <typename U = T, enable_if_t<is_copy_constructible<U>{}>> this wont work!

        template <typename U = T, enable_if_t<is_copy_constructible<U>{}>* = nullptr>
        void realloc_impl(priority_tag<1> pr)
        {
            cout << "copy construct n";
        };

        //template <typename U = T, enable_if_t<is_copy_constructible<U>{}>> this wont work!

        template <typename U = T, enable_if_t<is_nothrow_move_constructible<U>{}>* = nullptr>
            void realloc_impl(priority_tag<2> pr)
        {
            cout << "nothrow move n";
        };
        void realloc()
        {
                priority_tag<2> pr;
                realloc_impl(pr);
        }

        const static int val = is_nothrow_move_constructible<T>{} ? 1 : is_copy_constructible<T>{} ? 2 : 3;

        priority_tag<val> g;

};

class A {
public:
    A() = default;
    A(Aamp;amp;) noexcept = default;
};

class B {
public:
    B() = default;
    B(Bamp;amp;) = delete;
    B(const Bamp;) = default;
};

class C {
public:
    C() = default;
    C(Camp;amp;) {}
    C(const Camp;) = delete;
};


int main()
{
        my_vec<A> obj;
        obj.realloc();

        cout << obj.val;
}
  

Ответ №1:

Попробуйте скомпилировать приведенный ниже код

 template<void>
void foo(){}
  

Я получил ошибку компилятора ‘void’ не является допустимым типом для параметра, отличного от типа шаблона.


В качестве параметра шаблона вы можете передать:

1) введите, затем вы объявляете его, используя class / typename, как показано ниже:

 template< class/typename A[optional] = void>
void foo2(){}
  

2) нетипизированный
затем вы можете передать в качестве параметра шаблона некоторое промежуточное значение, указатели, ссылку на значение Lvalue и т.д. (полный список здесь)

 template<void*>
void foo3(){}
  

3) параметр типа шаблона

В вашем примере is_nothrow_move_constructible возвращает true для A , затем компилятор встречает строку:

 template <typename U = T, enable_if_t<is_nothrow_move_constructible<U>{}>>
  

что такое:

 template <typename U = T, void>
  

эта строка имеет неправильный синтаксис, и компилятор удаляет этот шаблон функции-члена из набора перегрузок.
Вы можете исправить это, объявив enable_if_t<is_nothrow_move_constructible<U>{} в качестве параметра типа:

 template <typename U = T, 
          typename   = enable_if_t<is_nothrow_move_constructible<U>{}> > // typename = void
    void realloc_impl(priority_tag<2> pr)
    {
        cout << "nothrow move n";
    };
  

или как нетип (указатель на void), что вы сделали в своем примере.

Комментарии:

1. Спасибо! Это имеет смысл