Подстановка аргументов шаблона завершается неудачно, и неявное преобразование не выполняется - PullRequest
1 голос
/ 26 февраля 2020
#include <type_traits>

template<bool Const>
struct view_tpl {
    using value_type = std::conditional_t<Const, const int, int>;
    value_type* ptr;

    view_tpl() = default;
    view_tpl(const view_tpl<false>& other) : ptr(other.ptr) { }
};

using view = view_tpl<false>;
using const_view = view_tpl<true>;

void read(const const_view& vw) { }

int main() {
    view vw;
    read(vw);
}

Этот код определяет константный и неконстантный тип представления, как псевдонимы шаблона view_tpl<Const>. Должно быть так, что view неявно конвертируется в const_view, но не наоборот.

Это Const равно true, определенный конструктор копирования это разрешает, и компилятор генерирует дополнительный конструктор копирования по умолчанию. Если Const равно false, то определенный конструктор копирования заменяет конструктор копирования по умолчанию.

Это неявное преобразование должно происходить при вызове f(vw).

В приведенном выше примере он работает правильно code.


Но если я добавлю аргумент в шаблоны (int N) и включу f и псевдонимы двух типов в шаблоны, он больше не будет работать:

#include <type_traits>

template<int N, bool Const>
struct view_tpl {
    using value_type = std::conditional_t<Const, const int, int>;
    value_type* ptr;

    view_tpl() = default;
    view_tpl(const view_tpl<N, false>& other) : ptr(other.ptr) { }
};

template<int N> using view = view_tpl<N, false>;
template<int N> using const_view = view_tpl<N, true>;

template<int N>
void read(const const_view<N>& vw) { }

int main() {
    view<0> vw;
    read(vw);
}

Вместо преобразования view_tpl<0, true> в view_tpl<0, false> компилятор пытается выполнить только прямую подстановку шаблона и завершается неудачно:

main.cpp: In function 'int main()':
main.cpp:20:12: error: no matching function for call to 'read(view<0>&)'
   20 |     read(vw);
      |            ^
main.cpp:16:6: note: candidate: 'template<int N> void read(const_view<N>&)'
   16 | void read(const const_view<N>& vw) { }
      |      ^~~~
main.cpp:16:6: note:   template argument deduction/substitution failed:
main.cpp:20:12: note:   template argument 'false' does not match 'true'
   20 |     read(vw);
      |            ^

Есть ли способ заставить эту работу не менять слишком много код? (Реальный код более сложный, чем этот пример)

Ответы [ 2 ]

1 голос
/ 26 февраля 2020

Мы могли бы явно привести и передать функцию read( const const_view<N> ), перегрузив оба типа.

#include <type_traits>

template<int N, bool Const>
struct view_tpl {
    using value_type = std::conditional_t<Const, const int, int>;
    value_type* ptr;

    view_tpl() = default;
    view_tpl(const view_tpl<N, false>& other) : ptr(other.ptr) { }
};

template<int N> using view = view_tpl<N, false>;
template<int N> using const_view = view_tpl<N, true>;

template<int N>
void read( const const_view<N>& vw )
{
    // 
}

template<int N>
void read( const view<N>& vw )
{
    const_view<N> vw_converted { vw };

    read( vw_converted );
}

int main() {
    view<0> vw;
    const_view<1> cvw;
    read(vw);
    read(cvw);
}

запустить в режиме онлайн

Или, если вы этого не сделаете обратите внимание на дополнительную копию для const_view

#include <type_traits>

template<int N, bool Const>
struct view_tpl {
    using value_type = std::conditional_t<Const, const int, int>;
    value_type* ptr;

    view_tpl() = default;
    view_tpl(const view_tpl<N, false>& other) : ptr(other.ptr) { }
};

template<int N> using view = view_tpl<N, false>;
template<int N> using const_view = view_tpl<N, true>;

template<int N, bool Const>
void read(const view_tpl<N,Const>& vw ) {
    view_tpl<N,true> inst { vw }; // Now it is const
                                  // But one extra copy for const_view
}

int main() {
    view<0> vw;
    const_view<1> cvw;
    read(vw);
    read(cvw);
}

запустить онлайн

1 голос
/ 26 февраля 2020

К сожалению, неявные преобразования не будут учитываться при выводе аргумента шаблона .

При выводе типа не учитываются неявные преобразования (кроме перечисленных выше настроек типа): это задача для разрешения перегрузки , которое происходит позже.

Вы можете указать аргумент шаблона явно, чтобы обойти вывод, тогда неявное преобразование будет работать нормально позже. например,

view<0> vw;
read<0>(vw);

Или применить явное преобразование и превратить его в помощника.

template<int N>
void read(const view<N>& vw) { read(static_cast<const_view<N>>(vw)); }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...