Шаблонный конструктор C ++ не будет компилироваться - PullRequest
6 голосов
/ 26 января 2010

Почему я не могу создать экземпляр объекта типа Foo с указанным выше конструктором?

У меня есть класс Bar, который использует внутренний typedef (как обходной путь для "template typedefs") и собираюсь использовать его в конструкторе, как показано ниже (CASE 1) Тем не менее, я не получаю его для компиляции. Это законный C ++? Случай 2, похоже, указывает на то, что проблема связана с typedef в Bar.

Как я могу определить конструктор, который будет принимать std :: vectors объектов с типом в Bar?

#include <vector>
#include <iostream>
#include <utility>

template <typename T>
struct Bar
{
    typedef std::pair<T, T> type; // or anything else that uses T
};

struct Foo
{
    Foo() {}

    // CASE 1: doesn't compile
    template <typename T> explicit Foo( const std::vector<typename Bar<T>::type>& data )
    {
        std::cout << "Hello\n";
    }

    //// CASE 2: compiles, but it's not what I want
    //template <typename T> explicit Foo( const std::vector<Bar<T> >& data )
    //{
    //  std::cout << "Hello\n";
    //}
};

int main()
{
    std::vector<Bar<int>::type> v; // for CASE 1
    //std::vector<Bar<int> > v; // for CASE 2

    Foo f( v );
    return 0;
}

Ответы [ 4 ]

9 голосов
/ 26 января 2010

В соответствии с пунктом 14.8.2.1 стандарта C ++, когда параметр шаблона используется только в не выводимом контексте, соответствующий аргумент шаблона не может быть выведен:

Если шаблон-параметр не используется ни в одном из параметров функции шаблона функции или используется только в не выводимом контексте, соответствующий ему шаблон-аргумент не может быть выведен из вызова функции, и шаблон-аргумент должен быть явно указан.

Определение не выводимых контекстов, как указано в §14.8.2.4:

Неопределенные контексты:

  • спецификатор вложенного имени типа, указанного с помощью qualid-id .

  • Тип, который представляет собой template-id , в котором один или несколько из template-arguments является выражением, которое ссылается на template-параметр .

В Bar<T>::type, Bar<T> является спецификатором вложенного имени и, следовательно, не выводимым контекстом, поэтому вы должны явно указать аргумент шаблона при вызове конструктора ... который не является возможно (т.е. вы не можете написать Foo f<int>(v)).

Полагаю, компилятор не может вывести аргумент шаблона, потому что это было бы по меньшей мере громоздко и, скорее всего, невозможно: представьте, что Bar специализирован:

template<typename T>
struct Bar
{
    typedef std::pair<T,T> type;
};

template<>
struct Bar<char>
{
    typedef std::pair<int,int> type;
};

Теперь у меня есть неоднозначность при вызове конструктора Foo с помощью std::vector<std::pair<int,int> >: должен ли аргумент шаблона быть int или char? И даже если бы не было такой неоднозначности, вы можете легко увидеть, что компилятору пришлось бы создавать экземпляры Bar с потенциально любым типом, прежде чем найти экземпляр с правильным typedef (ну, я не уверен, что приведенные выше утверждения действительно актуальны, так как я часто узнайте, что компиляторы намного умнее, чем я думал: -)!)

4 голосов
/ 26 января 2010

Ваш конструктор:

template <typename T> 
explicit 
Foo( const std::vector<typename Bar<T>::type>& data )

Аргумент шаблона T не может быть выведен из аргумента функции таким образом. (Я думаю, что это называется «не выводимым контекстом», но я не уверен.)

Это просто не сработает. Вам придется написать

template <typename B> 
explicit 
Foo( const std::vector<B>& data )

вместо этого и найдите другие способы заявить, что B имеет тип typename Bar<T>::type.

0 голосов
/ 26 января 2010

Может ли что-то подобное для вас работать?

#include <vector>
#include <iostream>
#include <utility>
#include <boost/static_assert.hpp>

template <typename T>
struct Bar
{
    typedef std::pair<T, T> type; // or anything else that uses T
    enum {val = 42};
};

template <typename T>
struct Traits
{
    enum {allowed = false};
};

template <typename T>
struct Traits<std::pair<T, T> >
{
    enum {allowed = true};
    typedef Bar<T> BarType;
};

struct Foo
{
    Foo() {}

    template <typename T> explicit Foo( const std::vector<T>& data )
    {
        BOOST_STATIC_ASSERT(Traits<T>::allowed);
        typedef typename Traits<T>::BarType BarType;
        std::cout << BarType::val << std::endl;
    }
};

int main()
{
    std::vector<Bar<int>::type> v;
    std::vector<float> v2;

    Foo f( v );
//    Foo f2( v2 ); // Compile error
    return 0;
}

Компилируется и работает на GCC 4.4.1. Вы можете специализировать Traits для других vector::value_type, которые разрешены вашим конструктором.

0 голосов
/ 26 января 2010

Я думаю, что единственная причина этого заключается в том, что вы хотите создать экземпляр Bar соответствующего типа вместо вывода "hello".

Возможно, вы можете попытаться отобразить типы в обратном направлении (вид обратного вывода, который, как вы надеетесь, сможет выполнить компилятор):

#include <utility>
#include <vector>

template <class T>
struct BarType;

template <class T>
struct BarType<std::pair<T, T> >
{
    typedef T type;
};

template <class T>
struct Bar {};

struct Foo
{
    template <class T>
    Foo(const std::vector<T>& )
    {
        Bar<typename BarType<T>::type> bar;
        //...
    }
};

int main()
{
    Foo(std::vector<std::pair<int, int> >());
}
...