Использование std :: conditional с is_class <int>, получение ошибки компиляции - PullRequest
1 голос
/ 29 апреля 2020

Я хочу иметь тип, который зависит от некоторого другого класса X, имеющего X :: value_type. Если внутри другого класса такого typedef нет, я хочу просто использовать X.

Поэтому я ищу код, подобный:

TypeChecker<X>::value_type  // Here, value_type might be X or X::value_type, depending on X.

Но мои первоначальные попытки терпят неудачу, см. ниже:

Вот программа, которая не компилируется:

#include<type_traits>

template<typename T>
struct TypeChecker {
    typedef typename std::conditional<
        std::is_class<T>::value,
        typename T::value_type,  // I believe the error is due to this line (could be wrong)
        T>::type value_type;
};

int main()
{
    TypeChecker<int>::value_type x = 3;

    return 0;
}

Она дает: error: 'int' is not a class, struct, or union type

Вы можете попробовать онлайн: Годболт ссылка

Я в замешательстве, потому что думал, что std::conditional выберет правильную ветвь, но, похоже, в некотором смысле оценивает их обоих. По крайней мере, достаточно, чтобы вызвать ошибку компиляции.

Справка?

Ответы [ 2 ]

2 голосов
/ 29 апреля 2020

std::conditional не "короткое замыкание". Чтобы экземпляр std::conditional<C,X,Y> даже был создан как тип класса, все три аргумента шаблона должны быть действительными типами. Нет способа получить шаблон, который можно использовать с одним из его параметров шаблона, представляющих недопустимый или неизвестный тип, даже если этот параметр не имеет прямого значения.

Некоторые решения для вашего TypeChecker:

// C++17, using partial specialization SFINAE:
template <typename T, typename Enable = void>
struct TypeChecker {
    using value_type = T;
};
template <typename T>
struct TypeChecker<T, std::void_t<typename T::value_type>> {
    using value_type = typename T::value_type;
};        

// C++11 or later, using function SFINAE:
template <typename T>
struct type_identity { using type = T; };
template <typename T>
type_identity<typename T::value_type> TypeChecker_helper(int); // not defined
template <typename T>
type_identity<T> TypeChecker_helper(...); // not defined
template <typename T>
struct TypeChecker {
    using value_type = typename decltype(TypeChecker_helper<T>(0))::type;
};

(Кроме того: я всегда немного подозрительно отношусь к шаблонам, которые автоматически «разворачивают» вещи, например TypeChecker. Это может помешать коду, который специально хочет использовать контейнер или итератор в качестве значения напрямую. )

2 голосов
/ 29 апреля 2020

Проблема в том, что T::value_type передается в std::conditional в качестве аргумента шаблона, когда T равно int, само выражение недопустимо.

Это можно сделать с помощью шаблона класса и частичной специализации. вместо этого.

template<typename T, typename = void>
struct TypeChecker {
    typedef T value_type;
};
template<typename T>
struct TypeChecker<T, typename std::enable_if<std::is_class<T>::value>::type> {
    typedef typename T::value_type value_type;
};

КСТАТИ: std::is_class, кажется, очень слабое состояние; Вы можете ограничить его, так как имеет тип члена value_type, например,

template<typename T, typename = void>
struct TypeChecker {
    typedef T value_type;
};
template<typename T>
struct TypeChecker<T, std::void_t<typename T::value_type>> {
    typedef typename T::value_type value_type;
};
...