у меня например
#include <iostream>
template <typename T>
struct Base {};
template <>
struct Base<std::string> {
static const int value = true;
};
template <>
struct Base<std::string &> {
static const int value = true;
};
int main() {
bool a = Base<std::string>::value;
bool b = Base<std::string &>::value;
std::cout << a << b << std::endl;
}
Примечание. У меня есть две идентичные специализации, и я хотел бы сократить их до одной. Я знаю два решения, от которых я бы предпочел не отказываться.
(1) Удалите ссылку на сайте вызова, чтобы требовалась только одна специализация.
(2) Создайте базовый класс и наследуйте от него версии ссылка и нет ссылки.
Есть ли третий вариант, когда специализация является общей по ссылочным и не ссылочным типам?
Требуются решения C++11.
Спасибо. 2 класса лучше, чем определение 3 классов, но это все равно два класса. Я хотел бы попробовать только один.





Это немного тяжело с enable_if, но я не думаю, что есть лучший способ.
#include <iostream>
#include <type_traits>
template <typename T, typename Enable = void>
struct Base {};
template <typename T>
struct Base<
T,
typename std::enable_if<
std::is_same<typename std::decay<T>::type, std::string>::value
>::type
>
{
static const int value = true;
};
int main() {
bool a = Base<std::string>::value;
bool b = Base<std::string &>::value;
std::cout << a << b << std::endl;
}
Вы можете выполнить проверку в контексте SFINAE:
// type trait to remove the lvalue-reference
template< class T > struct remove_lvalue_reference {typedef T type;};
template< class T > struct remove_lvalue_reference<T&> {typedef T type;};
template <typename T>
using remove_lvalue_reference_t = typename remove_lvalue_reference<T>::type;
template <typename T, typename = void>
struct Base {};
// valid when T is std::string and std::string&
template <typename T>
struct Base<T, typename std::enable_if<std::is_same<std::string, remove_lvalue_reference_t<T>>::value>::type> {
static const int value = true;
};
Я пришел к тому же ответу после того, как сам немного подумал об этом. :)
или, возможно, std::is_same<std::string, T>::value || std::is_same<std::string&, T>::value.
Когда я вернулся к своему коду, чтобы реализовать это, я обнаружил, что слишком сильно упростил. Настоящая проблема заключается в попытке использовать SFINAE на этом godbolt.org/z/IhGSEe, но я не думаю, что это будет возможно.
@bradgonesurfing Как насчет это? (Это кажется намного более сложным, хотя...)
Да, я думаю, это работает. Однако теперь он перешагнул порог более сложного, чем просто базовый класс, и наследуется с двумя случаями. Но спасибо за старание :)
1) Вроде нормально:
template <typename T>
struct BaseImpl {};
template <>
struct BaseImpl<std::string> {
static const int value = true;
};
template <typename T>
using Base = BaseImpl<typename std::remove_reference<T>::type>;
2) Кажется более подробным
template <typename T>
struct BaseImpl {};
template <>
struct BaseImpl<std::string> {
static const int value = true;
};
template <typename T>
struct Base : BaseImpl<T> {}; // or directly BaseImpl<std::remove_reference_t<T>>
template <typename T>
struct Base<T&> : BaseImpl<T> {};
3) Аналогично 2), менее подробно, но может быть сложнее
template <typename T>
struct Base : Base<T&> {};
template <typename T>
struct Base<T&> {};
template <>
struct Base : Base<std::string> {
static const int value = true;
};
1) кажется более читабельным, простым в реализации.
using Base = BaseImpl<std::remove_reference_t<T>>; должно быть using Base = BaseImpl<typename std::remove_reference<T>>;, так как OP ограничен C++11, но в остальном +1 для варианта 1
@NathanOliver: О, не видел тега С++ 11.
(3) наследовать ссылку без ссылки (или наоборот).