Улучшение сообщений об ошибках во время компиляции в шаблонных классах - PullRequest
0 голосов
/ 29 ноября 2018

У меня есть шаблонный класс, который должен принимать какие-то контейнеры (std :: array и std :: vector) для некоторого вида объектов (в этом примере string и doubles).

Myцель состоит в том, чтобы обеспечить некоторую явную ошибку компиляции, если класс построен из неправильной комбинации объектов.Следующий код компилируется в Visual Studio 17, версия 15.9.0.

///Performing checks 
namespace checks
{
    template <typename T>
    struct CorrectType {
        enum { value = false };
    };

    template <>
    struct CorrectType<std::string> {
        enum { value = true };
    };
    template <>
    struct CorrectType<double> {
        enum { value = true };
    };

    template <typename T>
    struct CorrectContainer {
        enum { value = false };
    };

    template <typename T, typename A>
    struct CorrectContainer<std::vector<T, A>> {
        enum { value = CorrectType<T>::value };
    };

    template <typename T, std::size_t N>
    struct CorrectContainer<std::array<T, N>> {
        enum { value = CorrectType<T>::value };
    };
    template <class Container>
    void constexpr check(){
        static_assert(checks::CorrectContainer<Container>::value, "Wrong container: only vectors/arrays of doubles/strings are accepted");
    }
}


template <typename Container>
class Wrapper
{
public:

    explicit Wrapper(const Container &container) :  container_(container), size_(container.size())
    {
  //type checking is performed
        checks::check<Container>();
    }

    void display() const {
        for (int i = 0; i < size_; i++)
            std::cout << this->container_[i] << " ";
        std::cout << std::endl;
    }

private:
    Container container_;
    int size_ = 0;
};

int main()
{
    //Ok
    Wrapper array_wrapper(std::array<double, 5>{0.0,1.0,2.0,3.0,4.0});
    array_wrapper.display();
    //Ok
    Wrapper string_wrapper(std::array<std::string, 3>{ "a","b","c" });
    string_wrapper.display();

    //Error - working as intended but not clear what went wrong
    Wrapper<std::vector<int>> vector_wrapper({ 1,2,3});
    vector_wrapper.display();
}

Приведенный выше код работает как задумано, но ошибка неоднозначна: мы не можем понять, является ли контейнер неправильным или типаобъекта содержится.Более того, если шаблонный объект не имеет функции-члена size, он потерпит неудачу слишком рано.

Ответы [ 2 ]

0 голосов
/ 29 ноября 2018

Вы можете переместить свой чек в класс (и разделить ваше состояние):

template <typename Container>
class Wrapper
{
    static_assert(checks::CorrectContainer<Container>::value,
                  "only std::vector/std::array allowed");
    static_assert(checks::CorrectType<typename Container::value_type>::value,
                  "only double and std::string");

public:
    // ...
};

Демо

0 голосов
/ 29 ноября 2018

Насколько я понимаю ваш запрос, обнаружение недопустимого типа, вероятно, можно набросать как

template<class> struct ValidType;

template<template<class...> class> struct ValidContainer: std::false_type {};
template<> struct ValidContainer<std::vector>: std::true_type {};

template<class> struct ValidType: std::false_type {};
template<class> struct ValidType<double>: std::true_type {};
template<class> struct ValidType<std::string>: std::true_type {};

template<class> struct ValidArgument {
    static_assert(false, "Both container and element type are wrong");
};  

template<template<class...> class Ctr, class T, class... Ts>
struct ValidArgument<Ctr<T, Ts...>> {
    static_assert(ValidContainer<Ctr>::value || ValidType<T>::value
            , "Both container and element type are wrong");
    static_assert(ValidContainer<Ctr>::value, "Container type is wrong");
    static_assert(ValidType<T>::value, "Element type is wrong");
};

template<class T, std::size_t n> struct ValidArgument<std::array<T, n>> {
    static_assert(ValidType<T>::value, "Element type is wrong");
};  

(Обратите внимание, что это не реальный код, а просто демонстрация идеи.)

Массивы по-прежнему злые, в том смысле, что std::array имеет параметр не-типа, и, таким образом, вы не можете иметь один шаблон, который проверяет контейнер, окончательная проверка по-прежнему для типа 0, с проверкой контейнера в общем случае.и std::array рассматривается отдельно.

В качестве альтернативы ValidType может быть несколько более компактным:

template<class T> using ValidType =             std::bool_constant<
        std::is_same_v<T, double> || std::is_same_v<T, std::string>>;

или

template<class T> using ValidType = std::disjunction<
        std::is_same<T, double>, std::is_same<T, std::string>>;

или менее стандартным типомсоответствующий класс.Например:

template<class T, class... Ts> inline constexpr bool is_one_of_v =
        std::disjunction_v<std::is_same<T, Ts>...>;
template<class T> using ValidType =
        std::bool_constant<is_one_of_v<T, double, std::string>>;

Таким образом, у вас меньше шансов получить мошеннические специализации позже.

...