Что означает токен «……»? т.е. оператор двойного многоточия в пакете параметров

110

Просматривая текущую реализацию новых заголовков C ++ 11 в gcc, я наткнулся на токен «......». Вы можете проверить, что следующий код компилируется нормально [через ideone.com].

template <typename T>
struct X
{ /* ... */ };

template <typename T, typename ... U>
struct X<T(U......)> // this line is the important one
{ /* ... */ };

Итак, что означает этот токен?

edit: Похоже, ТАК обрезано "......" в заголовке вопроса до "...", я действительно имел в виду "......". :)

Vitus
источник
подсказка: за ним ...следует ....
Alexandre C.
5
Разве это больше не похоже на то, что U...следует .... Тем не менее, очень странно.
edA-qa mort-ora-y
1
Примечание. Его можно найти в <functional>и <type_traits>всегда в контексте списка аргументов функции внутри параметра шаблона.
Potatoswatter
Единственный способ, которым я нашел, что он застрял в заголовке, - это поставить пробел между ними ... надеюсь, это сделает его более понятным для читателей.
Matthieu M.
@Matthieu M .: Спасибо, намного лучше!
Vitus

Ответы:

79

Каждый случай этой странности сопряжен со случаем обычного единственного многоточия.

  template<typename _Res, typename... _ArgTypes>
    struct _Weak_result_type_impl<_Res(_ArgTypes...)>
    { typedef _Res result_type; };

  template<typename _Res, typename... _ArgTypes>
    struct _Weak_result_type_impl<_Res(_ArgTypes......)>
    { typedef _Res result_type; };

  template<typename _Res, typename... _ArgTypes>
    struct _Weak_result_type_impl<_Res(_ArgTypes...) const>
    { typedef _Res result_type; };

  template<typename _Res, typename... _ArgTypes>
    struct _Weak_result_type_impl<_Res(_ArgTypes......) const>
    { typedef _Res result_type; };

Я предполагаю, что двойное многоточие по своему значению _ArgTypes..., ...похоже на расширение шаблона с переменным числом аргументов, за которым следует список переменных в стиле C.

Вот тест, подтверждающий эту теорию ... Я думаю, что у нас есть новый победитель наихудшего псевдооператора в истории.

Изменить: похоже, это соответствует. §8.3.5 / 3 описывает один способ сформировать список параметров как

список-объявлений-параметров opt ... opt

Таким образом, двойное многоточие формируется списком-объявлением параметров, заканчивающимся пакетом параметров, за которым следует еще одно многоточие.

Запятая не обязательна; §8.3.5 / 4 говорит

Если синтаксически правильно и где «...» не является частью абстрактного декларатора, «, ...» является синонимом «...».

Это находится в абстрактном-описателя, [править] , но Johannes делает хорошую точку , что они имеют в виду абстрактную-описателя в параметре-декларации. Интересно, почему они не сказали «часть объявления параметра» и почему это предложение не просто информативное примечание…

Кроме того, va_begin()in <cstdarg>требует параметра перед списком varargs, поэтому прототип, f(...)специально разрешенный C ++, бесполезен. Перекрестные ссылки с C99 недопустимы в простом C. Итак, это очень странно.

Примечание об использовании

По желанию, вот демонстрация двойного многоточия:

#include <cstdio>
#include <string>

template< typename T >
T const &printf_helper( T const &x )
    { return x; }

char const *printf_helper( std::string const &x )
    { return x.c_str(); }

template< typename ... Req, typename ... Given >
int wrap_printf( int (*fn)( Req... ... ), Given ... args ) {
    return fn( printf_helper( args ) ... );
}

int main() {
    wrap_printf( &std::printf, "Hello %s\n", std::string( "world!" ) );
    wrap_printf( &std::fprintf, stderr, std::string( "Error %d" ), 5 );
}
Potatoswatter
источник
Да это правильно. T (U ..., ...), однако, тоже хорошо компилируется; возможно, они хотели сэкономить место. :)
Vitus
1
Но что бы это значило? И как компилятор может определить, где заканчивается _ArgTypes и начинаются некоторые «лишние» параметры?
Bo Persson
12
@Bo Persson: std::is_function«s valueдолжно быть истинным , даже если функция С одной переменной длины и потому , что T (U ...) это не соответствует такой функции, вам нужно это безумие. Например, int f (int, char, ...) точно соответствует T (U ......) с T = int, U = {int, char} и токеном "..." varargs.
Vitus
4
«Это находится в абстрактном-описателе» -> они не означают часть абстрактного описателя последнего параметра того же список типов параметров. Например, void (int...)здесь, ...не является частью абстрактного декларатора int, следовательно, он является синонимом void(int, ...). Если бы вы написали void(T...)и Tявляется пакетом параметров шаблона, ...он будет частью абстрактного декларатора и, следовательно, не будет эквивалентен void(T, ...).
Йоханнес Шауб - лит
2
«Кроме того, va_begin () в <cstdarg> требует параметра перед списком varargs, поэтому прототип f (...), специально разрешенный C ++, бесполезен». - Бесполезно только в том случае, если вы хотите знать, какие аргументы были переданы. f(...)широко используется как перегрузка резервной функции в метапрограммировании шаблонов, где эта информация не нужна (и где функция даже не вызывается).
4

на vs2015 разделение запятой необходимо в версии шаблона:

    template <typename T, typename ... U>
    struct X<T(U...,...)> {};// this line is the important one

пример создания:

    X<int(int...)> my_va_func;

С уважением, FM.

Красная волна
источник
Я это тоже только что заметил, до сих пор бывает. Отчет об ошибке на сайте developercommunity.visualstudio.com/content/problem/437260/… .
egyik
Хорошо знать. Есть ссылки или цитаты на стандарты по этому поводу?
Red.Wave
.سلام ببخشید نمیدانم
egyik
Это публичный форум. Пусть люди читают то, что вы думаете. Пожалуйста, сохраните родной язык для личных сообщений. سپاس.
Red.Wave
Хорошо, тогда. Я не эксперт по стандарту - я думаю, что другие подробно рассмотрели его выше. Если кто-то захочет прокомментировать отчет о проблеме Microsoft, он может повысить его приоритет. В отчете показано, что clang и gcc разрешают то, что не позволяет VC ++, поэтому я думаю, что мы, вероятно, на довольно сильной позиции.
egyik