#c #templates #coercion
Вопрос:
Предположим, у меня есть шаблон функции, подобный этому:
template<class T>
inline
void
doStuff(T* arr)
{
// stuff that needs to use sizeof(T)
}
Затем в другом .h
файле у меня есть класс шаблона Foo
, который имеет:
public: operator T*() const;
Теперь я понимаю, что это разные вещи. Но если у меня есть переменная Foo<Bar> f
в стеке, единственный способ принудить ее к какому-либо указателю-это вызвать operator T*()
. Тем не менее, если позвонить doStuff(f)
, GCC жалуется, что doStuff
не может Foo<Bar>
вместо автоматического использования оператора T*()
принудить Bar*
, а затем специализировать шаблон функции с Bar
помощью as T
.
Могу ли я что-нибудь сделать, чтобы это работало с двумя шаблонами? Или либо аргумент функции шаблона должен быть реальным типом указателя, либо класс шаблона с оператором принуждения должен быть передан функции, не являющейся шаблоном?
Ответ №1:
GCC прав. В аргументах шаблона учитываются только точные совпадения, преобразования типов-нет. Это связано с тем, что в противном случае пришлось бы учитывать бесконечное (или, по крайней мере, экспоненциальное) количество преобразований.
Если Foo<T> является единственным другим шаблоном, в котором вы собираетесь работать, лучшим решением было бы добавить:
template<typename T> inline void doStuff(const Foo<T>amp; arr) {
doStuff(static_cast<T*>(arr));
}
Если у вас возникла эта проблема с большим количеством шаблонов, этот должен ее исправить:
#include <boost/type_traits/is_convertible.hpp>
#include <boost/utility/enable_if.hpp>
template<template <typename> class T, typename U> inline typename boost::enable_if<typename boost::is_convertible<T<U>, U*>::type>::type doStuff(const T<U>amp; arr) {
doStuff(static_cast<U*>(arr));
}
Хотя это немного многословно 😉
Ответ №2:
Возможно, это стоит попробовать:
doStuff<Bar>(f);
Я думаю, что это заставит компилятор ожидать, что T* будет Bar*, а затем использовать оператор Foo T*() для выполнения приведения, но я не могу сказать, что пробовал это.
Комментарии:
1. Это правильно. Компилятор жалуется, потому что вывод аргументов шаблона не удается. Как только вы приведете аргумент, в нем не будет ничего особенного. На этом этапе компилятор знает как целевой, так и конечный тип последовательности преобразования.
Ответ №3:
Идея Леона, вероятно, лучшая. Но в крайнем случае вы также можете явно вызвать оператор приведения:
doStuff(static_cast<Bar*>(f));
Ответ №4:
Я не уверен, почему преобразование не работает, но вы можете использовать перегрузку, чтобы устранить проблему
template
inline
void
doStuff(Tamp; arrRef)
{
doStuff(amp;arrRef);
}
Комментарии:
1. Разыменование ссылки — это вообще плохой стиль. Я знаю, что хаки имеют свою ценность, но используются они крайне редко.
2. Кроме того, это просто неверно. Синтаксически, потому что вы нигде не определили T, семантически, потому что Foo<T>.операторamp;() вернет не T*, а Foo<T><T>*.
Ответ №5:
Ну, T* не является отдельным типом от T в том смысле, в каком вы думаете. Указатель является определителем типа. Я не уверен, что об этом говорится в стандарте, но я бы сказал, что, поскольку переменная уже относится к типу T, она не пытается преобразовать снова. Если вы хотите выполнить некоторые пользовательские действия для получения указателя, перегрузите операторamp;.
Комментарии:
1. Вы действительно не хотите перегружать оператораamp;(). Причины этого см. в разделе «Рекомендации Google».