конструктор обобщенных копий во внутреннем классе - PullRequest
5 голосов
/ 11 сентября 2011

Я хотел бы указать преобразование из A<T>::B в A<U>::B.

template<typename T>
struct A
{
    struct B
    {
        B() {}

        template<typename U>
        B(const typename A<U>::B& rhs) {}
    };
};

int main()
{
    A<int>::B x;
    A<double>::B y = x;
}

Я думал, что это сделает это, но я получаю ошибку компилятора:

преобразование из ‘A :: B’ в нескалярный тип 101 A :: B ’запрашивается»

Почему мой код неверен и как правильно достичь желаемого преобразования?

Ответы [ 3 ]

4 голосов
/ 11 сентября 2011

Шаблон не может быть конструктором копирования.§12.8 / 2, сноска:

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

Поскольку аргумент вашего шаблона равен const &, ее подпись будет точно такой же, как неявно объявленная функция в случае копирования, поэтому она никогда не будет использоваться в качестве конструктора копирования.

Это может быть нормально, потому что в примере, который вы используетеэто как преобразователь конструктор.Тем не менее, аргументы шаблона перед :: представляют собой не выводимый контекст, поэтому компилятор не может подключить A<int>::B и разрешить int.Из-за различных способов специализации шаблонов у компилятора нет возможности выяснить, какой A, если таковой имеется, отвечает требованиям.Вы можете добавить typedef A<int>::B B; внутри A<float>, и тогда int и float будут квалифицироваться как U.

. Это можно исправить, используя SFINAE и добавляя типы членов в классы, чтобы помочьперемещаться по иерархии.Вот демоверсия .

#include <typeinfo>
#include <iostream>

template<typename T>
struct A
{
    typedef T type;

    struct B
    {
        B() {}

        template<typename U>
        B(const U& rhs, typename U::nest_A_parent * = NULL ) {
            std::cout << "copied from type "
            << typeid( typename U::nest_A_parent::type ).name() << '\n';
        }

    private:
        typedef A nest_A_parent;
        template< typename U >
        friend struct B;
    };
};

int main()
{
    A<int>::B x;
    A<double>::B y( x );
}
3 голосов
/ 11 сентября 2011

Слегка исправленный источник, чтобы продемонстрировать, что речь идет о границах системы вывода типов:

template<typename T>
struct A
{
    struct B
    {
        B() {}

        template<typename U>
            B(const typename A<U>::B& rhs) {}

        template<typename U>
            B& operator=(const typename A<U>::B& rhs) {}

        template<typename U>
            B& something(const typename A<U>::B& rhs) {}
    };
};

int main()
{
    A<int>::B x;

    A<double>::B y(x);     // fails to deduce
    A<double>::B y = x;    // fails to deduce
    A<double>::B y; y = x; // fails to deduce

    x.something(y);        // fails to deduce
    x.something<double>(y);// NO PROBLEM
}

Вы видите, что когда мы немного помогаем компилятору, проблем больше нет.Также обратите внимание, что фактическая ошибка компилятора (gcc) показывает, что это путаница:

test.cpp|24 col 15| note: candidate is:
test.cpp|15 col 44| note: template<class U> A<T>::B& A<T>::B::something(const typename A<U>::B&) 
 [with U = U, T = int, A<T>::B = A<int>::B, typename A<U>::B = A<T>::B]

Обратите внимание на часть, где U = U (то есть, не устранена)

2 голосов
/ 11 сентября 2011

Как сделать преобразователь конструктор (как указал Potatoswatter, он не может быть конструктором копирования по определению), который соответствует только вложенному типу B, для любого A<T>::B:

namespace detail {

// private type to identify all A<T>::B
struct B {};

} // detail

// trait to identify all A<T>::B
// a template alias could also be used here
template<typename T>
struct is_b: std::is_base_of<detail::B, T> {};

template<typename T>
struct A
{
    struct B: detail::B {
        B() {}

        template<typename U>
        B(U&& u)
        {
            static_assert( is_b<typename std::decay<U>::type>::value
                           , "Conversion only allowed from A<T>::B" );
        }
    };
};

Преимущество этого метода в том, что он не использует SFINAE, поэтому о неверной попытке преобразования будет сообщено через static_assert, а не при молчаливом сбое.С другой стороны, вам понадобится SFINAE, если есть хотя бы один конструктор преобразования шаблонов.

Предполагается, что A<T>::B будет отличаться от A<U>::B (для разных T и U).Если вложенные типы идентичны (как это имеет место в вашем упрощенном примере кода), вам лучше будет определить B в другом месте и использовать typedef some_private_namespace::B B; в A.

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