шаблоны и операторы приведения

Этот код компилируется в CodeGear 2009 и Visual Studio 2010, но не в gcc. Почему?

class Foo
{
public:
    operator int() const;

    template <typename T> T get() const { return this->operator T(); }
};

Foo::operator int() const
{
    return 5;
}

Сообщение об ошибке:

test.cpp: в функции-члене `T Foo :: get () const ':
test.cpp: 6: error:' const class Foo 'не имеет члена с именем' operator T '


person Jonathan Swinney    schedule 19.04.2010    source источник
comment
Что ж, если вы потребуете преобразование во что-то другое, кроме int, это не сработает. Как ты им пользуешься?   -  person GManNickG    schedule 20.04.2010
comment
Я даже не дохожу до того, где его можно использовать ... он ломается до этого.   -  person Jonathan Swinney    schedule 20.04.2010
comment
Вы вообще куда-нибудь вызываете Foo :: get ()?   -  person JoeG    schedule 20.04.2010


Ответы (4)


Это ошибка G ++. operator T - неквалифицированное зависимое имя (потому что в нем есть T, и поиск, таким образом, будет отличаться в зависимости от его типа). Таким образом, он должен быть найден при создании экземпляра. Стандартные правила

Два имени одинаковы, если

  • ...
  • это имена определяемых пользователем функций преобразования, сформированных с одним и тем же типом.

Таким образом, имя типа, указанное после ключевого слова operator, не обязательно должно совпадать лексически. Вы можете применить следующий обходной путь, чтобы заставить GCC рассматривать его как зависимое имя.

template<typename T, typename>
struct identity { typedef T type; };

class Foo
{
public:
    operator int() const;

    template <typename T> T get() const { 
      return this->identity<Foo, T>::type::operator T(); 
    }
};
person Johannes Schaub - litb    schedule 19.04.2010
comment
Это действительно работает, но кажется неудобным решением. Спасибо за вашу помощь! - person Jonathan Swinney; 20.04.2010
comment
@Jonathan, да, и он ломается с виртуальным operator int. Вы можете сделать static_cast<typename identity<Foo, T>::type*>(this)->operator T();, если хотите сделать виртуальный звонок. - person Johannes Schaub - litb; 20.04.2010

Я не уверен, каковы точные правила для имен операторов C ++, но я считаю, что он пытается вызвать operator T() вместо operator int(). Почему бы просто не использовать гипс:

template <typename T> T get() const { return static_cast<T>(*this); }

Я не тестировал это, но считаю, что это даст более или менее то же самое. Если нет, должен быть способ сделать это без прямого вызова operator T(). В конце концов, для этого и нужны перегруженные операторы.

person Chris Lutz    schedule 19.04.2010
comment
@GMan - Не был уверен, оставил его, потому что у UncleBens он уже есть без него. - person Chris Lutz; 20.04.2010
comment
Вызывает ли static_cast явно перегруженный оператор литья? Я хочу избежать случайного вызова int вместо long или double вместо float и т. Д. - person Jonathan Swinney; 20.04.2010
comment
Похоже, это не так. Я это тестировал. - person Jonathan Swinney; 20.04.2010

Если я немного изменю код на это:

template <typename T> T get() const { return operator T(); }

С GCC я получаю следующее:

нет аргументов для 'operator T', которые зависят от параметра шаблона, поэтому должно быть доступно объявление 'operator T' (если вы используете '-fpermissive', G ++ примет ваш код, но разрешит использование необъявленного имени устарел)

Но если бы вы использовали -fpermissive, все сработало бы, только если T = int

С другой стороны, почему бы не сделать:

template <typename T> T get() const { return *this; }

В этом суть оператора преобразования.

person UncleBens    schedule 19.04.2010
comment
Потому что я хочу явно вызвать оператор для определенного типа, не позволяя компилятору вызывать float, когда я имел в виду double или int, когда имел в виду long. - person Jonathan Swinney; 20.04.2010
comment
@Jonathan: Я не вижу причин широко использовать функции преобразования, особенно если вы хотите хорошо отслеживать, что во что конвертируется. - person UncleBens; 20.04.2010

Мне немного любопытно, как это использовать. Я полагаю, это так, чтобы вы могли сказать:

Foo f;
int x = f.get<int>();

но почему бы не сказать:

Foo f;
int x = static_cast<int>(f);

который немного длиннее для ввода (хотя можно использовать обозначение C приведения), но не требует прыжков через обруч.

person Community    schedule 19.04.2010