I came accross this question when I wanted to split a variadic template parameter pack into two parts, the first containing all elements but the last and a second one containing only the last one. A straight forward implementation, which came to my mind, was the invoke1 function in the following example:
template <typename... Ts>
void invoke1(Ts... ts, int param) {
}
template <typename... Ts>
void invoke2(int param, Ts... ts) {
}
int main() {
invoke1(1); // this works
invoke2(1); // this works
invoke1(1, 2, 3); // this does not work
invoke1<int, int>(1, 2, 3); // this works
invoke2(1, 2, 3); // this works
return 0;
}
Why are the template parameters not deduced for invoke1 when the template parameter pack is specified first? Would it create ambiguities in type deduction?
template <typename... Ts>
void invoke1(Ts... ts, int param) {
}
First of all, why does
invoke1<int, int>(1, 2, 3);
work? The above specifies the types explicitly, so no type deduction is required. The template is instantiated as:
void invoke1(int, int, int);
so a call with (1, 2, 3) now becomes perfectly valid.
On the other side if you don't specify the types explicitly, the compiler has no way to know where the parameter pack ends.
Is it invoke1(int, int, int, int); or invoke1(int, int, int);?
Now, you'll say: "Can't it just take the last given parameter and end the parameter pack before it?". Well, the answer is no.
invoke2 on the other hand works fine, because it's clear where the parameter pack starts and ends.
As a rule of thumb, always put the parameter pack last.
Related
I understand that, given a braced initializer, auto will deduce a type of std::initializer_list, while template type deduction will fail:
auto var = { 1, 2, 3 }; // type deduced as std::initializer_list<int>
template<class T> void f(T parameter);
f({ 1, 2, 3 }); // doesn't compile; type deduction fails
I even know where this is specified in the C++11 standard: 14.8.2.5/5 bullet 5:
[It's a non-deduced context if the program has] A function parameter for which the associated argument is an initializer list (8.5.4) but the parameter
does not have std::initializer_list or reference to possibly cv-qualified std::initializer_list
type. [ Example:
template void g(T);
g({1,2,3}); // error: no argument deduced for T
—end example ]
What I don't know or understand is why this difference in type deduction behavior exists. The specification in the C++14 CD is the same as in C++11, so presumably the standardization committee doesn't view the C++11 behavior as a defect.
Does anybody know why auto deduces a type for a braced initializer, but templates are not permitted to? While speculative explanations of the form "this could be the reason" are interesting, I'm especially interested in explanations from people who know why the standard was written the way it was.
There are two important reasons for templates not to do any deduction (the two that I remember in a discussion with the guy in charge)
Concerns about future language extensions (there are multiple meanings you could invent - what about if we wanted to introduce perfect forwarding for braced init list function arguments?)
The braces can sometimes validly initialize a function parameter that is dependent
template<typename T>
void assign(T &d, const T& s);
int main() {
vector<int> v;
assign(v, { 1, 2, 3 });
}
If T would be deduced at the right side to initializer_list<int> but at the left side to vector<int>, this would fail to work because of a contradictional argument deduction.
The deduction for auto to initializer_list<T> is controversial. There exist a proposal for C++-after-14 to remove it (and to ban initialization with { } or {a, b}, and to make {a} deduce to the type of a).
The reason is described in N2640:
A {}-list cannot deduce against a plain type parameter T. For example:
template<class T> void count(T); // (1).
struct Dimensions { Dimensions(int, int); };
size_t count(Dimensions); // (2).
size_t n = count({1, 2}); // Calls (2); deduction doesn't
// succeed for (1).
Another example:
template<class T>
void inc(T, int); // (1)
template<class T>
void inc(std::initializer_list<T>, long); // (2)
inc({1, 2, 3}, 3); // Calls (2). (If deduction had succeeded
// for (1), (1) would have been called — a
// surprise.)
On the other hand, being able to deduce an initializer_list<X> for T is attractive to
allow:
auto x = { 1, 1, 2, 3, 5 };
f(x);
g(x);
which was deemed desirable behavior since the very beginning of the EWG discussions about
initializer lists.
Rather than coming up with a clever deduction rule for a parameter type T matched with a {}-list (an option we pursued in earlier sketches and drafts of this paper), we now prefer to handle this with a special case for "auto" variable deduction when the initializer is a {}-list. I.e., for the specific case of a variable declared with an "auto" type specifier and a {}-list initializer, the "auto" is deduced as for a function f(initializer_list<T>) instead of as for a function f(T).
For conclusion, the problem is that if we allow a {}-list to deduce against a plain type parameter T, then the function with parameter T would have very high priority during overload resolution, which may cause wired behavior (like the examples above).
First of all it's "speculative explanations of the form "this could be the reason"" as you call it.
{1,2,3} is not only std::initializer_list<int> but also allow initialize types without constructor. For example:
#include <initializer_list>
struct x{
int a,b,c;
};
void f(x){
}
int main() {
f({1,2,3});
}
is correct code. To show that it isn't initializer_list let's see the following code:
#include <initializer_list>
struct x{int a,b,c;};
void f(x){
}
int main() {
auto il = {1, 2, 3};
f(il);
}
Error is:
prog.cpp: In function ‘int main()’:
prog.cpp:10:9: error: could not convert ‘il’ from ‘std::initializer_list<int>’ to ‘x’
And now to the question "What is the difference?"
in auto x = {1, 2, 3}; code it's OK to determine type, because coder explicitly said "It's not important what's type it is" using auto
While in case of function template he may be sure that he is using different type. And it's good to prevent errors in ambiguous cases (It doesn't seem like C++ style , through).
Especially bad it will be in case when there was 1 function f(x) and then it was changed to template one. Programmer wrote to use it as x, and after adding new function for other type it slightly change to call completely different one.
I'm trying to create a struct template with a variadic template type pack, that can deduct the sum of the size of all types passed in.
Below you find a simplified example, in the real-world context, the size computed is used to create further member objects.
template <typename... Types>
struct OverallSize
{
template <typename FirstType, typename... NextTypes>
static constexpr size_t sizesum() { return sizeof (FirstType) + sizesum<NextTypes...>(); }
template <typename LastType>
static constexpr size_t sizesum() { return sizeof (LastType); }
static constexpr size_t size = sizesum<Types...>();
};
// Should work e.g. like this
auto s = OverallSize<int, float, char>::size; // s will be 9 on x86-64
I'm used to this recursive parameter unpacking approach when it comes to argument lists and assumed this works as well with argument-less functions and explicit template specification. However I get the following error when compiling with clang
Call to 'sizesum' is ambiguous
...
Candidate function [with FirstType = unsigned long, NextTypes = <>]
Candidate function [with LastType = unsigned long]
So it seems as if the last recursion iteration doesn't work here – not sure why the compiler doesn't simply chose the most obvious choice: The one with only one template type – just as it would happen if there was an actual template argument passed to the function.
So, what do I have to do to make this compile and work as desired?
For C++14 you can use SFINAE:
template <
typename FirstType,
typename... NextTypes,
std::enable_if_t<sizeof...(NextTypes) >= 1>* = nullptr >
static constexpr size_t sizesum() {
return sizeof (FirstType) + sizesum<NextTypes...>();
}
this template will be considered only if parameters pack has size >= 1.
Demo
I have the following construct:
template <class... Args>
class some_class
{
public:
some_class() = default;
some_class(Args...) = delete;
~some_class() = default;
};
template<>
class some_class<void>
{
public:
some_class() = default;
~some_class() = default;
};
The reason for this is that I just want to allow the users to create objects using the default constructor, so for example:
some_class<int,float> b;
should work but
some_class<int,float> c(1,3.4);
should give me a compilation error.
At some point in time I also needed to create templates based on void hence, the specialization for void:
some_class<void> a;
But by mistake I have typed:
some_class<> d;
And suddenly my code stopped compiling and it gave me the error:
some_class<Args>::some_class(Args ...) [with Args = {}]’ cannot be
overloaded
some_class(Args...) = delete;
So here comes the question: I feel that I am wrong that I assume that some_class<> should be deduced to the void specialization... I just don't know why. Can please someone explain why some_class<> (ie: empty argument list) is different from some_class<void>? (A few lines from the standard will do :) )
https://ideone.com/o6u0D6
void is a type like any other (an incomplete type, to be precise). This means it can be used as a template argument for type template parameters normally. Taking your class template, these are all perfectly valid, and distinct, instantiations:
some_class<void>
some_class<void, void>
some_class<void, void, void>
some_class<void, char, void>
In the first case, the parameter pack Args has one element: void. In the second case, it has two elements: void and void. And so on.
This is quite different from the case some_class<>, in which case the parameter pack has zero elements. You can easily demonstrate this using sizeof...:
template <class... Pack>
struct Sizer
{
static constexpr size_t size = sizeof...(Pack);
};
int main()
{
std::cout << Sizer<>::size << ' ' << Sizer<void>::size << ' ' << Sizer<void, void>::size << std::endl;
}
This will output:
0 1 2
[Live example]
I can't really think of a relevant part of the standard to quote. Perhaps this (C++11 [temp.variadic] 14.5.3/1):
A template parameter pack is a template parameter that accepts zero or more template arguments. [ Example:
template<class ... Types> struct Tuple { };
Tuple<> t0; // Types contains no arguments
Tuple<int> t1; // Types contains one argument: int
Tuple<int, float> t2; // Types contains two arguments: int and float
Tuple<0> error; // error: 0 is not a type
—end example ]
For example, I cannot write this:
class A
{
vector<int> v(12, 1);
};
I can only write this:
class A
{
vector<int> v1{ 12, 1 };
vector<int> v2 = vector<int>(12, 1);
};
Why is there a difference between these two declaration syntaxes?
The rationale behind this choice is explicitly mentioned in the related proposal for non static data member initializers :
An issue raised in Kona regarding scope of identifiers:
During discussion in the Core Working Group at the September ’07 meeting in Kona, a question arose about the scope of identifiers in the initializer. Do we want to allow class scope with the possibility of forward lookup; or do we want to require that the initializers be well-defined at the point that they’re parsed?
What’s desired:
The motivation for class-scope lookup is that we’d like to be able to put anything in a non-static data member’s initializer that we could put in a mem-initializer without significantly changing the semantics (modulo direct initialization vs. copy initialization):
int x();
struct S {
int i;
S() : i(x()) {} // currently well-formed, uses S::x()
// ...
static int x();
};
struct T {
int i = x(); // should use T::x(), ::x() would be a surprise
// ...
static int x();
};
Problem 1:
Unfortunately, this makes initializers of the “( expression-list )” form ambiguous at the time that the declaration is being parsed:
struct S {
int i(x); // data member with initializer
// ...
static int x;
};
struct T {
int i(x); // member function declaration
// ...
typedef int x;
};
One possible solution is to rely on the existing rule that, if a declaration could be an object or a function, then it’s a function:
struct S {
int i(j); // ill-formed...parsed as a member function,
// type j looked up but not found
// ...
static int j;
};
A similar solution would be to apply another existing rule, currently used only in templates, that if T could be a type or something else, then it’s something else; and we can use “typename” if we really mean a type:
struct S {
int i(x); // unabmiguously a data member
int j(typename y); // unabmiguously a member function
};
Both of those solutions introduce subtleties that are likely to be misunderstood by many users (as evidenced by the many questions on comp.lang.c++ about why “int i();” at block scope doesn’t declare a default-initialized int).
The solution proposed in this paper is to allow only initializers of the “= initializer-clause” and “{ initializer-list }” forms. That solves the ambiguity problem in most cases, for example:
HashingFunction hash_algorithm{"MD5"};
Here, we could not use the = form because HasningFunction’s constructor is explicit.
In especially tricky cases, a type might have to be mentioned twice. Consider:
vector<int> x = 3; // error: the constructor taking an int is explicit
vector<int> x(3); // three elements default-initialized
vector<int> x{3}; // one element with the value 3
In that case, we have to chose between the two alternatives by using the appropriate notation:
vector<int> x = vector<int>(3); // rather than vector<int> x(3);
vector<int> x{3}; // one element with the value 3
Problem 2:
Another issue is that, because we propose no change to the rules for initializing static data members, adding the static keyword could make a well-formed initializer ill-formed:
struct S {
const int i = f(); // well-formed with forward lookup
static const int j = f(); // always ill-formed for statics
// ...
constexpr static int f() { return 0; }
};
Problem 3:
A third issue is that class-scope lookup could turn a compile-time error into a run-time error:
struct S {
int i = j; // ill-formed without forward lookup, undefined behavior with
int j = 3;
};
(Unless caught by the compiler, i might be intialized with the undefined value of j.)
The proposal:
CWG had a 6-to-3 straw poll in Kona in favor of class-scope lookup; and that is what this paper proposes, with initializers for non-static data members limited to the “= initializer-clause” and “{ initializer-list }” forms.
We believe:
Problem 1: This problem does not occur as we don’t propose the () notation. The = and {} initializer notations do not suffer from this problem.
Problem 2: adding the static keyword makes a number of differences, this being the least of them.
Problem 3: this is not a new problem, but is the same order-of-initialization problem that already exists with constructor initializers.
One possible reason is that allowing parentheses would lead us back to the most vexing parse in no time. Consider the two types below:
struct foo {};
struct bar
{
bar(foo const&) {}
};
Now, you have a data member of type bar that you want to initialize, so you define it as
struct A
{
bar B(foo());
};
But what you've done above is declare a function named B that returns a bar object by value, and takes a single argument that's a function having the signature foo() (returns a foo and doesn't take any arguments).
Judging by the number and frequency of questions asked on StackOverflow that deal with this issue, this is something most C++ programmers find surprising and unintuitive. Adding the new brace-or-equal-initializer syntax was a chance to avoid this ambiguity and start with a clean slate, which is likely the reason the C++ committee chose to do so.
bar B{foo{}};
bar B = foo();
Both lines above declare an object named B of type bar, as expected.
Aside from the guesswork above, I'd like to point out that you're doing two vastly different things in your example above.
vector<int> v1{ 12, 1 };
vector<int> v2 = vector<int>(12, 1);
The first line initializes v1 to a vector that contains two elements, 12 and 1. The second creates a vector v2 that contains 12 elements, each initialized to 1.
Be careful of this rule - if a type defines a constructor that takes an initializer_list<T>, then that constructor is always considered first when the initializer for the type is a braced-init-list. The other constructors will be considered only if the one taking the initializer_list is not viable.
I've got a variant class. It has a pair of constructors:
/// Construct and fill.
template <typename T>
inline
variant (const T& t)
{
YYASSERT (sizeof (T) <= S);
new (buffer.raw) T(t);
}
template <typename T>
inline
variant (T&& t)
{
YYASSERT (sizeof (T) <= S);
new (buffer.raw) T(std::move(t));
}
Now I've called those constructors in this code:
parser::symbol_type
parser::make_IDENTIFIER (const Wide::ParsedFile::Identifier*& v)
{
return symbol_type (token::IDENTIFIER, v);
}
symbol_type takes a variant as it's second argument in this specific constructor, and v is being implicitly converted.
However, MSVC will try to use the rvalue reference constructor instead of using the other constructor, resulting in a compilation error when it attempts to new a reference. Why is that, and how can I make it stop?
You generally should not overload a templated T&& function. You should instead have the single function which forwards:
template <typename T>
inline
variant (T&& t)
{
typedef typename std::remove_reference<T>::type Tr;
YYASSERT (sizeof (Tr) <= S);
new (buffer.raw) Tr(std::forward<T>(t));
}
This has the functionality of your two overloads, while avoiding the problem of picking the wrong one.
I believe (not positive) that these are the two variants in your overload set:
varaint<const Wide::ParsedFile::Identifier*>(const Wide::ParsedFile::Identifier*const&)
varaint<const Wide::ParsedFile::Identifier*&>(const Wide::ParsedFile::Identifier*&)
And the second one wins because it is more specialized than the first (I'm making an educated guess, I'm not 100% positive).
The second template would be a better match, because the const specifiers are in different places in your function and in the first constructor.
In the first overload you will have T being deduced as
const Wide::ParsedFile::Identifier*
And then creating a const reference to that type. That adds an extra const.