Is substitution performed on a variadic parameter pack type if the pack is empty?
I believe I have found the relevant piece of standardese. §14.8.2p7 says:
The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations.
Since EnableIf<dependent_true_type<T>>
is used in a template parameter declaration, substitution should occur and this is a bug in clang.
Dependent non-type parameter packs: what does the standard say?
The code is ill-formed, no diagnostic is required.
If std::is_signed_v<T>
, then std::enable_if_t<std::is_signed_v<T>>
denotes the type void
. Otherwise, std::enable_if_t<std::is_signed_v<T>>
does not denote a valid type. Therefore, every valid specialization of myAbs
requires an empty template parameter pack.
Per [meta.rqmts]/4, the program has undefined behavior if std::enable_if
is specialized. Therefore, the aforementioned behavior cannot be changed.
In my opinion
IsSigned< T >...
is a dependent template parameter,
therefore it can not be checked against §17.7 (8.3) in template
definition time.IsSigned< T >
could be for examplevoid
for one
subset ofT
s,int
for another subset or substitution failure. For
thevoid
subset it is true, that the empty template parameter pack
would be the only valid specialization, but theint
subset could
have many valid specializations. It depends on the actualT
argument.
The compiler cannot check it, in the same way it cannot, say, solve an arbitrary equation for you. NDR (no diagnostic required) is made exactly for such cases — the program is ill-formed and would require a diagnostic if the compiler is actually capable of detecting that. NDR permits the compiler not to check it.
When N is zero, the instantiation of the expansion produces an empty
list. Such an instantiation does not alter the syntactic
interpretation of the enclosing construct.
The rule we are talking about is a semantic rule, not a syntactic rule, because syntactic rules are in [gram].
So what is the rationale for the NDR rules? In general, they address problems that are not reproducible among implementation strategies. For example, they may cause the code to misbehave in some implementation strategies, but do not cause any problems (and cannot be easily) in others.
Also, note that the standard talks in terms of program with terms like "ill-formed". Therefore, it is not always plausible to talk about the well-formed ness of an isolated code snippet. In this case, std::enable_if
is required not to be specialized, but the situation may get more complicated otherwise.
incorrect deduction of template parameter pack
Template errors are hard to get right. It's just a quality of implementation. Clang for instances gives
main.cpp:2:6: note: candidate template ignored: substitution failure
[with dim = 2, N = 3, P = true, C = false]: deduced incomplete pack <int, int, (no value)>
for template parameter 'ParametersType'
which is easier to understand. And yes, unless using auto
, {stuff}
has no type.
Template parameter pack deduction when not passed as last parameter
We can block deduction:
template<typename... Args>
void testfunc(const block_deduction<std::function<void (float, Args..., char)>>& func)
with
template<class T>
struct tag_t{using type=T;};
template<class T>
using block_deduction=typename tag_t<T>::type;
and now Args...
is in a non-deduced context.
You can do fancier things with SFINAE and omitting char
then testing that char
is at the end of the Args...
, but that seems overkill.
I will bet dollars to donuts that when gcc and clang disagree with MSVC, MSVC isn't right. But I have not standard delved to confirm that.
C++ Variadic template empty pack
You don't need any specializations of get
, just
template<typename... An>
tuple<An...> get()
{
return tuple_cat(getSingle<An>()...);
}
Check if parameter pack contains a type
No, you have to use (partial) specialization with variadic templates to do compile-time computations like this:
#include <type_traits>
template < typename Tp, typename... List >
struct contains : std::true_type {};
template < typename Tp, typename Head, typename... Rest >
struct contains<Tp, Head, Rest...>
: std::conditional< std::is_same<Tp, Head>::value,
std::true_type,
contains<Tp, Rest...>
>::type {};
template < typename Tp >
struct contains<Tp> : std::false_type {};
There is only one other intrinsic operation for variadic templates and that is the special form of the sizeof operator which computes the length of the parameter list e.g.:
template < typename... Types >
struct typelist_len
{
const static size_t value = sizeof...(Types);
};
Where are you getting "it has serious compilation-time overhead" with boost mpl from? I hope you are not just making assumptions here. Boost mpl uses techniques such as lazy template instantiation to try and reduce compile-times instead of exploding like naive template meta-programming does.
Clang fails to expand parameter pack in std::function instantiation
TL;DR: it's a clang bug, but there's also a bug in the standard.
First be aware that in C++, templates are handled in two steps:
- Constructs that are not dependent on the template parameters are built when the enclosing template is defined.
- Constructs that are dependent are built when the enclosing template is instantiated.
Now, it seems that clang treats std::function< void(Int<Ts>...) >
as a non-dependent type, with the following reasoning:
Int<Ts>
is a non-dependent type (correct).- Thus, pack expansion containing
Int<Ts>
(i.e.Int<Ts>...
) is also a non-dependent "type" (?). - Because all components of
void(Int<Ts>...)
are non-dependent, it is a non-dependent type (obviously incorrect). - Because the name
std::function
is non-dependent and the template argumentvoid(Int<Ts>...)
is a non-dependent non-pack-expansion type,std::function< void(Int<Ts>...) >
is a non-dependent type.
(Note that the "non-pack-expansion" check makes it different from the Tuple
case.)
Therefore, when Foo
is defined, the type name Function
is treated as naming a non-dependent type, and is immediately built, and pack expansion (which happens during instantiation) is not accounted. Consequently, all uses of Function
is replaced with the "desugared" type std::function< void(int) >
.
Furthermore, clang has the notion of instantiation-dependent, which means the construct is not dependent, but it still somehow involves the template parameters (e.g. the construct is only valid for some parameters). std::function< void(Int<Ts>...) >
is treated as a instantiation-dependent type, so when the template is instantiated, clang still performs substitution for using Function = std::function< void(Int<Ts>...) >
. As a result, Function
gets the correct type, but this does not propagate to uses of Function
in the definition of Foo
.
Now heres the bug in the standard.
Whether a type is dependent is defined in [temp.dep.type]:
A type is dependent if it is
- a template parameter,
- a member of an unknown specialization,
- a nested class or enumeration that is a dependent member of the current instantiation,
- a cv-qualified type where the cv-unqualified type is dependent,
- a compound type constructed from any dependent type,
- an array type whose element type is dependent or whose bound (if any) is value-dependent,
- a function type whose exception specification is value-dependent,
- denoted by a simple-template-id in which either the template name is a template parameter or any of the template arguments is a dependent
type or an expression that is type-dependent or value-dependent or is
a pack expansion [ Note: This includes an injected-class-name of a
class template used without a template-argument-list. — end note ] ,
or- denoted by
decltype(expression)
, where expression is type-dependent.
Note that it doesn't say that a function type whose parameter list contains pack expansions is a dependent type, only that "a compound type constructed from any dependent type" and "a function type whose exception specification is value-dependent" are dependent. Neither is helpful here.
How can a template parameter pack have both explicit and deduced arguments?
It's [temp.arg.explicit]/8:
Template argument deduction can extend the sequence of template arguments corresponding to a template parameter pack, even when the sequence contains explicitly specified template arguments. [ Example:
template<class ... Types> void f(Types ... values);
void g() {
f<int*, float*>(0, 0, 0); // Types is deduced to the sequence int*, float*, int
}
— end example ]
Related Topics
Visual Studio 2013 Fatal Error C1041 /Fs
What Does Iterator->Second Mean
How to Iterate Through Every Possible Combination of N Playing Cards
Is It a Good Practice to Always Use Smart Pointers
Boost::Asio Synchronous Client with Timeout
Getting Gdb to Save a List of Breakpoints
What Modern C++ Libraries Should Be in My Toolbox
How and When to Align to Cache Line Size
Is a Struct's Address the Same as Its First Member's Address
Restrict Passed Parameter to a String Literal
Project Euler Problem 12 - C++
What Is the Purpose of Std::Make_Pair VS the Constructor of Std::Pair
Vs: Unexpected Optimization Behavior with _Bitscanreverse64 Intrinsic