Есть ли способ проверить количество аргументов std::initializer_list во время компиляции?

Я пытаюсь создать функцию, которая принимала бы несколько аргументов заданного типа, но как тип, так и количество аргументов должны быть указаны через шаблоны.

Я обнаружил, что использование initializer_list в C++11, вероятно, является хорошей техникой в ​​этом случае, но можно ли проверить его размер во время компиляции? Есть ли другие методы, которые могут решить эту проблему?

#include <initializer_list>

// Here I want to define type and number of components for each point

template <typename T, int DIM>
class Geometry
{
public:
    void addPoint(std::initializer_list<T> coords)
    {
        assert(coords.size() == DIM); // Working good, but not compile-time

        // Next line does not compile because size() is not known at compile-time
        static_assert(coords.size() == DIM, "Wrong number of components"); 
    }
};

person Dmitry Shurov    schedule 24.12.2015    source источник
comment
Почему-то этот вопрос постоянно всплывает. Здесь его прогрызли до костей.   -  person SergeyA    schedule 24.12.2015
comment
Если вы хотите вызвать addPoint, используя синтаксис списка инициализаторов, решением будет использовать template<std::size_t N> void addPoint(const T (& coords)[N]). Затем вы можете static_assert(N == DIM, "") в теле функции. Однако это было относительно недавно указано в черновике Стандарта (год назад), поэтому Clang пока не поддерживает его (похоже, будет в 3.8.0). Он работает в GCC (достаточно последних версиях) и Visual C++ 2015.   -  person bogdan    schedule 25.12.2015
comment
@bogdan: Хорошее решение! Хотя, насколько я вижу, у него есть недостаток: когда я пытаюсь передать целое число вместо базового типа double типа geo.addPoint({1.0, 2});, я получаю следующую ошибку: mismatched types ‘double’ and ‘int’. Шаблоны Variadic позволили мне не только проверять строгое равенство типов, но и выполнять преобразование типов.   -  person Dmitry Shurov    schedule 25.12.2015
comment
Похоже на ошибку в GCC - он пытается вывести тип элемента, когда этого не следует делать.   -  person bogdan    schedule 25.12.2015


Ответы (3)


Вы не можете статически утверждать количество во время выполнения. И количество значений в initializer_list определяется во время выполнения вызывающей стороной функции.

Даже создание вашей функции constexpr не сработает, поскольку вычисление функции не требуется во время компиляции.

Вместо этого вы должны использовать вариативный шаблон.

person Nicol Bolas    schedule 24.12.2015
comment
Спасибо за разъяснения! Никогда раньше не имел дело с вариативными шаблонами, но ваш ответ побудил меня изучить эту тему. Опубликовал мое решение ниже. Не уверен, что это довольно элегантно, но решает проблему. - person Dmitry Shurov; 25.12.2015

Благодаря Nicol я обратил внимание на вариативные шаблоны. Проблема заключалась не только в проверке количества аргументов, но и в проверке конвертируемости их типов в базовый тип. Вот мое решение, основанное на эта и эта темы. Он работает, как и ожидалось, в GCC 4.9.

template<class T, class...>
struct are_convertible : std::true_type
{};

template<class T, class U, class... TT>
struct are_convertible<T, U, TT...>
    : std::integral_constant<bool, std::is_convertible<T,U>{} && are_convertible<T, TT...>{}>
{};

template <typename T, int DIM>
class Geometry
{
public:
    template<typename... Args>
    void addPoint(Args... coords)
    {
        static_assert(sizeof...(coords) == DIM, "Number of components does not match template");
        static_assert(are_convertible<T, Args...>{}, "All arguments' types must be convertible to the template type"); 
    }
};
person Dmitry Shurov    schedule 24.12.2015
comment
std::is_convertible<T,U> проверяет, можно ли преобразовать T в U, а не наоборот. Также имейте в виду, что списки инициализаторов запрещают сужение преобразований, а это решение — нет. - person bogdan; 25.12.2015

Добавление точки класса POD с элементами данных DIM лучше всего не соответствует вашей цели. Тогда неявный вызов конструктора убедится, что все в порядке.

person TheROE    schedule 24.12.2015
comment
Не уверен, что понимаю, что вы имеете в виду. Не могли бы вы привести пример? - person Dmitry Shurov; 25.12.2015