Специализируйте шаблон для типов, содержащих typedef

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

Моя проблема в том, что я не вижу, как отрицать SFINAE. Очевидно, я мог бы исключить особый случай для неспециального параметра, но я не знаю, как исключить вариант по умолчанию для специального параметра.

Поэтому я попробовал частичную специализацию следующим образом:

struct NormalType { };

struct SpecialType { typedef int special; };

template <typename T, typename IsSpecial = void>
struct DetectSpecial {
    void detected() { std::cout << "Not special...\n"; }
};

template <typename T>
struct DetectSpecial<T, typename T::special> {
    void detected() { std::cout << "Special!\n"; }
};

но специализация не используется (как SSCCE на ideone).

Я также рассматривал возможность использования enable_if, но не понимаю, как его использовать для правильных и неправильных выражений, а не true/false.

Каков самый простой способ определить DetectSpecial по-разному для типов, содержащих определенный typedef (значение typedef может быть любым; важно его присутствие)?

О, я все еще застрял с некоторыми компиляторами C++03. В любом случае, я не думаю, что что-то изменится в SFINAE.


person Jan Hudec    schedule 24.02.2014    source источник
comment
Это не сработает, потому что T::special не void. Когда вы создаете экземпляр DetectSpecial<SpecialType>, вы создаете экземпляр DetectSpecial<SpecialType, void>, который не соответствует DetectSpecial<SpecialType, int> (частичная специализация).   -  person dyp    schedule 24.02.2014


Ответы (2)


Требуемое минимальное изменение — вставить некоторое выражение в специализацию, которое зависит от T::special и дает void (чтобы соответствовать аргументу по умолчанию). Например:

template<class T>
struct void_alias
{
    typedef void type;
};

template <typename T>
struct DetectSpecial<T, typename void_alias<typename T::special>::type> {
    void detected() { std::cout << "Special!\n"; }
};
person dyp    schedule 24.02.2014

Может помочь следующее: (C++11) https://ideone.com/XISlZ6 (C++03) https://ideone.com/egKrcL

#include <cstdint>

template <typename U>
class has_special
{
private:
    template<typename T> struct dummy;
    template<typename T>
    static std::uint8_t check(dummy<typename T::special>*);
    template<typename T> static std::uint16_t check(...);
public:
    static
    constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t);
};
person Jarod42    schedule 24.02.2014
comment
За исключением того, что я получил предупреждение о том, что параметры шаблона по умолчанию для шаблонов функций являются расширением С++ 11, и мне нужно, чтобы оно работало и в компиляторе С++ 03. - person Jan Hudec; 24.02.2014
comment
@dyp: Очевидно, сегодня у меня умственный блок, так как я не понял, как легко сделать указатель, чтобы получить что-то, что всегда можно построить. - person Jan Hudec; 24.02.2014
comment
@JanHudec Это не работает, если T::special является ссылкой. (Но вы можете использовать тот же трюк с void_alias или другим псевдонимом.) - person dyp; 24.02.2014