the following code snippet won't compile under gcc4.6.1:
template <typename... TS>
void do_stuff(TS... ts)
{
auto f = [](TS... things) { };
}
It throws an error stating that the pack things was not expanded. The following code does compile however:
template <typename... TS>
void do_stuff(TS... ts)
{
auto f = [](TS... things...) { };
}
Notice the extra unpacking operator after things inside the parameter list. I've never seen a situation where a variadic pack had to be expanded during its declaration. So my question to you kind folks is:
Is this legal C++0x syntax (the snippet that compiles) or is it just a quirk with GCC when it comes to dealing with variadic types?
Two things:
Yes, GCC is wrong to reject [](TS... things) { }. It's possible that it hasn't been implemented yet.
What you declared by [](TS ... things...) { } is equivalent to [](TS... things, ...). In C++ (not in C), you can leave off the comma before the C-style variadic ellipsis. So instead of doing void printf(char const *fmt, ...) you can declare void printf(char const *fmt...). That's what happens in your lambda. The first ellipsis is the parameter pack unpacking, and the second ellipsis is the C-style variadic ellipsis.
Related
I am very unclear when to use non-type template arguments (C++20) or normal arguments in constexpr functions. It's unclear to me, what the restrictions are and when to switch from pure parameters to non-type template parameters (see Live).
Here an example which illustrates the clunkiness with normal arguments:
template<typename Tuple, typename Pred>
constexpr auto getLambda(Tuple&& tuple, Pred&& pred)
{
return [=](auto I, auto J) {
return pred(std::get<I>(tuple), std::get<J>(tuple));
};
}
template<typename T>
struct A
{
constexpr A(T t) : val(t){};
T val;
};
int main()
{
static constexpr auto t = std::make_tuple(A{10.0}, A{1});
constexpr auto p = [](auto&& a, auto&& b) { return a.val < b.val; };
constexpr auto s = getLambda(t, p);
//constexpr auto b = s(1,0); // that does unfortunately not compile -> go back write differently... :-|||
}
Mostly I first try to use normal arguments like above, and after cumbersome fiddling with compile errors about non-constant expressions, try an approach with template<auto t> (non-type template paramters. Mostly having then two implementations one for each use-case (this seems stupid to me...)
Its sounds to me that modern generic programming with C++20 tends towards compile-time computations using constexpr together with some type-meta-programming.
Could anyone shed some light into this rather new "dark-corner" of C++. I probably misunderstand
when something is not a constant-expression and when it is...
The short version: Use non-type template parameter to set non-type template arguments (more general everywhere, where you need a constant expression) and normal arguments for everything else.
The thing about constexpr functions you always have to keep in mind is that they can also be called at runtime. So every normal argument is not necessarily a constant expression. Hence you cannot use it to provide a non-type template argument (as the I in std::get<I>).
Of course one could argue that when called to calculate a constexpr variable the passed arguments are always constant expressions and could be used as such also inside the function. But it would be unexpected if a constexpr function works at compile time but not anymore at runtime.
One could expect that with the new consteval keyword in C++20, one could use normal arguments to consteval functions in constant expressions, since we know that these arguments have to be constant expressions. But this does not seem to be the case: https://godbolt.org/z/guz7FQ Why this is the case I do not know. But in general I like the seperation between normal variables and non-type template arguments.
I noticed, that both GCC and MSVC are happy with the following code:
#include <iostream>
void foo(...);
int main()
{
foo();
}
void foo(...)
{
std::cout << "foo\n";
}
More specifically, code was run under GCC 6.2.0 and Visual Studio 2015.
I know that C requires at least one named parameter preceding the ellipsis, which allows to handle any number of arguments using specialized va_start, va_args, and va_end macros from <stdarg.h> (here <cstdarg>) header. Otherwise, it won't even compile.
Does C++ have some special treatment for "pure ellipsis" form or is it not suitable for fetching the arguments, i.e. it's allowed, but completely impractical?
C++ Variadiac arguments are explained here. This syntax is supported in C++, but the arguments are not accessible:
In the C programming language, at least one named parameter must appear before the ellipsis parameter, so printz(...); is not valid.
In C++, this form is allowed even though the arguments passed to such
function are not accessible, and is commonly used as the fallback
overload in SFINAE, exploiting the lowest priority of the ellipsis
conversion in overload resolution. This syntax for variadic arguments
was introduced in 1987 C++ without the comma before the ellipsis. When
C89 adopted function prototypes from C++, it replaced the syntax with
one requiring the comma. For compatibility, C++98 accepts both
C++-style f(int n...) and C-style f(int n, ...)
In C++ it is allowed because even if there is no named parameter before, the ... will only be an inaccessible variadic argument.
In C, there is no overloads, and having a function that receives only ... can be a great source of runtime error. Inaccessible varargs is not useful is C.
In C++, it is currently use as a sink function for sfinae. Compilers will always choose other overload if possible before resolving a call to a function with variadic parameter. It is pratical for sfinae purpose:
template<typename F, typename... Ts>
struct is_callable {
private:
template<typename T, typename... Args>
static decltype(
static_cast<void>(std::declval<T>()(std::declval<Args>()...)),
std::true_type{}
) test(int);
template<typename...>
static std::false_type test(...); // not a template variadic, classic vararg here.
public:
// Here, the compiler will try the first version of the function
// Because '...' is not the preferred overload
// If the return type expression don't yield to a type, the compiler
// will have no choice but to pick the variadic one,
// resulting in a std::false_type
using type = decltype(test<F, Ts...>(0));
};
This question already has answers here:
What is the meaning of "... ..." token? i.e. double ellipsis operator on parameter pack
(2 answers)
Closed 8 years ago.
While looking at this question I found myself in the cpp reference site where I noticed a strange and new to me syntax :
template<class Ret, class... Args>
struct is_function<Ret(Args......)volatile &&> : std::true_type {};
Yep, 6 dots ! Initially I thought this was a typo, but after checking the libstdc++ source again there it was eg at line 444 :
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes......) volatile &&> : public true_type { };
Is this a valid syntax ? Dot dot dot, are used to pack and unpack parameter packs ? What do 6 dots do ?
Why does libstdc++ use ... ... in it's implementation of is_function? If we check out the cppreference section for std::is_function it gives a sample implementation and says for the first ... ... case:
// specialization for variadic functions such as std::printf
template<class Ret, class... Args>
struct is_function<Ret(Args......)> : std::true_type {};
so we need the second set of ... to match a variadic function like printf:
Comma optional as per 8.3.5 [dcl.fct]
|
v
Ret(Args... ...)
^ ^
| |
Match a function with a variable number of arguments
|
and the function is a variadic function
Note, we have functions like fprintf that two arguments before the variadic terms and we need to match those as well.
Indeed if we use that implementation and attempt to match printf without the ... ... specialization then it fails see it live.
This corner of the language is covered in this post C++11's six dots:
I was mucking around the other day and discovered this nice little oddity:
template <typename... Args>
void foo(Args......);
As it turns out, ...... can be totally valid C++11. This is what happens when backward compatibility mixes with new hotness.
// These are all equivalent.
template <typename... Args> void foo1(Args......);
template <typename... Args> void foo2(Args... ...);
template <typename... Args> void foo3(Args..., ...);
Hopefully the last one shows what is happening here. [...]
Why is this valild? We can see that , ... is synonymous with ... from the draft C++11 standard section 8.3.5 [dcl.fct] which has the following grammar:
parameter-declaration-clause:
parameter-declaration-listopt...opt
parameter-declaration-list , ...
and says:
[...] Where syntactically correct and where “...” is not part of
an abstract-declarator, “, ...” is synonymous with “...”. [...]
In this case, the two are for different purposes. The first is for parameter pack expansion and the second is for variable argument lists. That particular declaration is to handle functions which take some regular parameters plus a variable argument list.
The difference is between run-time and compile-time variability. A function which takes a variable number of arguments at run-time is special. It is a single function which can handle a variable number of arguments from the caller:
void f(int x,...) // equivalent to void f(int x ...)
{
// Do some run-time logic here to determine what to
// do with parameters after x.
}
This is distinct from the notion that we want to be able to have a template which uses a variety of functions with various parameters which are known at compile time. For example, we could define a function template which takes a pointer to a function and allow the number and types of the arguments to vary:
template <typename... Args>
void g(void (*function_ptr)(Args...))
{
// We can do logic here to call function_ptr with the proper
// number of arguments.
}
Given these functions:
void f1(int);
void f2(int,float);
You can call g with any of them:
g(f1); // fine
g(f2); // also fine
However
g(f); // error
The compiler wouldn't know what to use for the Args parameter pack in g.
This question already has answers here:
What is the meaning of "... ..." token? i.e. double ellipsis operator on parameter pack
(2 answers)
Closed 8 years ago.
While looking at this question I found myself in the cpp reference site where I noticed a strange and new to me syntax :
template<class Ret, class... Args>
struct is_function<Ret(Args......)volatile &&> : std::true_type {};
Yep, 6 dots ! Initially I thought this was a typo, but after checking the libstdc++ source again there it was eg at line 444 :
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes......) volatile &&> : public true_type { };
Is this a valid syntax ? Dot dot dot, are used to pack and unpack parameter packs ? What do 6 dots do ?
Why does libstdc++ use ... ... in it's implementation of is_function? If we check out the cppreference section for std::is_function it gives a sample implementation and says for the first ... ... case:
// specialization for variadic functions such as std::printf
template<class Ret, class... Args>
struct is_function<Ret(Args......)> : std::true_type {};
so we need the second set of ... to match a variadic function like printf:
Comma optional as per 8.3.5 [dcl.fct]
|
v
Ret(Args... ...)
^ ^
| |
Match a function with a variable number of arguments
|
and the function is a variadic function
Note, we have functions like fprintf that two arguments before the variadic terms and we need to match those as well.
Indeed if we use that implementation and attempt to match printf without the ... ... specialization then it fails see it live.
This corner of the language is covered in this post C++11's six dots:
I was mucking around the other day and discovered this nice little oddity:
template <typename... Args>
void foo(Args......);
As it turns out, ...... can be totally valid C++11. This is what happens when backward compatibility mixes with new hotness.
// These are all equivalent.
template <typename... Args> void foo1(Args......);
template <typename... Args> void foo2(Args... ...);
template <typename... Args> void foo3(Args..., ...);
Hopefully the last one shows what is happening here. [...]
Why is this valild? We can see that , ... is synonymous with ... from the draft C++11 standard section 8.3.5 [dcl.fct] which has the following grammar:
parameter-declaration-clause:
parameter-declaration-listopt...opt
parameter-declaration-list , ...
and says:
[...] Where syntactically correct and where “...” is not part of
an abstract-declarator, “, ...” is synonymous with “...”. [...]
In this case, the two are for different purposes. The first is for parameter pack expansion and the second is for variable argument lists. That particular declaration is to handle functions which take some regular parameters plus a variable argument list.
The difference is between run-time and compile-time variability. A function which takes a variable number of arguments at run-time is special. It is a single function which can handle a variable number of arguments from the caller:
void f(int x,...) // equivalent to void f(int x ...)
{
// Do some run-time logic here to determine what to
// do with parameters after x.
}
This is distinct from the notion that we want to be able to have a template which uses a variety of functions with various parameters which are known at compile time. For example, we could define a function template which takes a pointer to a function and allow the number and types of the arguments to vary:
template <typename... Args>
void g(void (*function_ptr)(Args...))
{
// We can do logic here to call function_ptr with the proper
// number of arguments.
}
Given these functions:
void f1(int);
void f2(int,float);
You can call g with any of them:
g(f1); // fine
g(f2); // also fine
However
g(f); // error
The compiler wouldn't know what to use for the Args parameter pack in g.
I don't understand how the following feature should be used. When I call A::f I can omit the template parameters, but I don't understand why.
template <typename... Args>
struct A
{
template <Args...>
void f() {}
};
int main()
{
A<int, bool> a;
a.f();
}
To be specific, what does the template <Args...> mean and why can I leave the template parameters out of the function call to f?
template<typename ...Args> is a variadic template. It means, that you can specify any number of template type parameters, but I think you already know this.
Whenever Args... appears in the template code, it will be unpacked ("expanded") to the types of the instantiation. In your example, this is int, bool. So your class becomes, when fully expanded, this definition:
struct A<int, bool>
{
template <int, bool>
void f() {}
};
This means, A<int,bool>::f() is again templated (the arguments are unpacked into another template declaration, as you called it), but this time with non-type template parameters with the types int and bool (they're anonymous), so you could instantiate f() for example like this:
a.f<1, true>();
Unfortunately, g++ seems to have a bug and won't accept this piece of code, while it accepts your code.
clang accepts both codes. I expect that in your code, clang doesn't care if the int and bool template parameters are omitted, but it doesn't complain either when they are specified (in contrast to g++).
Usage example:
If you want to use the specified values, they can't be anonymous (obviously). You can, for example, provide a format string in f() which is used to "printf" the template values, like this:
template <Args ...values>
void f(const char *fmt) {
std::printf(fmt, values...);
}
Then, the following code
A<int> a;
a.f<42>("The answer is %d!\n");
will print:
The answer is 42!
But, using the syntax from above (Args... being expanded to anonymous non-type template parameters), the template parameters are essentially useless.
Without specifying values, it still compiles (which surprised me!) and prints an uninitialized int value.
The reason you can leave the template arguments out is because c++11 has template argument deduction template<args ...> is a variadic template which is basically a pack of possibly different typenames. This website explains the omission of template arguments. From reading further into the deduction of non type template arguments, which the compiler can deduce I believe that the compiler is realizing that they are never used (with clang at least) and making it's deduction based on that, read the deducing non type template arguments portion of the website