Consider the following code:
#include <utility>
#include <type_traits>
#include <cstddef>
#include <iostream>
template <typename>
struct A
{
void get() {} // #1
};
template <typename ...Ts>
struct B : A<Ts>... {};
template <typename ...Ts>
struct std::tuple_size<B<Ts...>> : std::integral_constant<std::size_t, 2> {};
template <std::size_t I, typename ...Ts>
struct std::tuple_element<I, B<Ts...>>
{
using type = int;
};
template <std::size_t I, typename ...Ts>
constexpr int get(B<Ts...>) // #2
{
return 2;
}
int main()
{
B<double, long long> b;
auto [x, y] = b;
std::cout << x << ' ' << y << std::endl;
}
GCC accepts the above code and outputs 2, 2 as I excepted (godbolt), but MSVC complains (godbolt).
error C2385: ambiguous access of 'get'
According to cppreference:
For each identifier, a variable whose type is "reference to
std::tuple_element<i, E>::type" is introduced: lvalue reference if its
corresponding initializer is an lvalue, rvalue reference otherwise.
The initializer for the i-th variable is
e.get<i>(), if lookup for the identifier get in the scope of E by
class member access lookup finds at least one declaration that is a
function template whose first template parameter is a non-type
parameter
Otherwise, get<i>(e), where get is looked up by
argument-dependent lookup only, ignoring non-ADL lookup.
From my understanding, the member get() (line #1) does not meet the quoted requirements (it's even not a template), and the compiler should choose the non-member get() (line #2). GCC works as I excepted, but MSVC seems stuck at the member get(), ignoring whether it is eligible.
Which compiler is correct? Why does MSVC find ineligible get() in structured binding?
Related
I am trying to compile this code with gcc and clang:
#include <iostream>
#include <type_traits>
template<int N>
struct Test
{
template<typename = std::enable_if_t<N == 1, bool>>
void func()
{
std::cout << "Test::func" << std::endl;
}
};
int main()
{
Test<0> t;
//t.func();
}
So, I have an error:
error: no type named 'type' in 'std::__1::enable_if<false, bool>'; 'enable_if' cannot be used to
disable this declaration
template <bool _Bp, class _Tp = void> using enable_if_t = typename enable_if<_Bp, _Tp>::type;
But if I compile that code with vc++ there is no error.
So, which compiler resolves this issue according to c++ standard?
GCC and Clang are correct here.
When you write Test<0> that will substitute N with 0. In the declaration of func the std::enable_if_t will try to access the member ::type which does not exist. That's an error because that member does not exist and you cannot have a declaration as such.
The way that SFINAE works is that, during overload resolution, when the compiler performs template argument deduction, if the substitution of an argument in the template fails, you don't get an error and the function is discarded from the potential overload set. On your example, there is no template argument deduction. N is already known from the class template and because of that the declaration of func is ill-formed before overload resolution even begins.
A potential workaround for your use case is this:
template<int M = N, typename = std::enable_if_t<M == 1, bool>>
void func()
{
std::cout << "Test::func" << std::endl;
}
That way, func now depends on a function template argument, M, and SFINAE works as expected.
The title is a bit confusing but what I mean is this specific case:
template<class>
struct get_type_of_nontype;
template<class T, T Value, template<T> class Template>
struct get_type_of_nontype<Template<Value>> {
using type = T;
};
So I can use it like this:
#include <type_traits>
template<int I>
class int_non_type {};
static_assert(
std::is_same<typename get_type_of_nontype<int_non_type<0>>::type, int>::value,
"T is deduced to be `int` as `template<T> class Template` is `template<int> class int_non_type`"
);
This works fine in C++17. In C++14 I get the following errors:
gcc 8:
<source>:5:8: error: template parameters not deducible in partial specialization:
struct get_type_of_nontype<Template<Value>> {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:5:8: note: 'T'
clang 7:
<source>:5:8: error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used [-Wunusable-partial-specialization]
struct get_type_of_nontype<Template<Value>> {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:4:16: note: non-deducible template parameter 'T'
template<class T, T Value, template<T> class Template>
^
And then they both complain that struct get_type_of_nontype<int_non_type<0>> is incomplete, so typename get_type_of_non_type<int_non_type<0>>::type can't compile.
Why is this different between C++14 and C++17? Is this just a compiler bug? If not, is there a way to do this in C++14?
The Standard wording in [temp.deduct.type] paragraphs 13 and 14 changed. So yes, your example is invalid in C++14, but is allowed in C++17 thanks to a new language feature.
C++14:
A template type argument cannot be deduced from the type of a non-type template-argument.
[Example:
template<class T, T i> void f(double a[10][i]);
int v[10][20];
f(v); // error: argument for template-parameter T cannot be deduced
-- end example]
C++17:
When the value of the argument corresponding to a non-type template parameter P that is declared with a dependent type is deduced from an expression, the template parameters in the type of P are deduced from the type of the value. [Example:
template<long n> struct A { };
template<typename T> struct C;
template<typename T, T n> struct C<A<n>> {
using Q = T;
};
using R = long;
using R = C<A<2>>::Q; // OK; T was deduced to long from the
// template argument value in the type A<2>
-- end example] The type of N in the type T[N] is std::size_t. [Example:
template<typename T> struct S;
template<typename T, T n> struct S<int[n]> {
using Q = T;
};
using V = decltype(sizeof 0);
using V = S<int[42]>::Q; // OK; T was deduced to std::size_t from the type int[42]
-- end example]
[Example:
template<class T, T i> void f(int (&a)[i]);
int v[10];
void g() {
f(v); // OK: T is std::size_t
}
-- end example]
This seems a bit related to another C++17 template change: C++17 is the first version to allow placeholder types in non-type template parameters, as in template <auto Value> or template <auto* Ptr>. I'd expect compiler implementations would need some similar logic for supporting both of the two language features.
Amazed (and cofused) by a similar question I tried myself the example that the mentioned question found in the standard:
template <typename T, typename U = int> struct S;
template <typename T = int, typename U> struct S
{ void f() { std::cout << __PRETTY_FUNCTION__ << '\n'; } };
int main()
{
S s; s.f();
return 0;
}
The code above prints void S<int, int>::f() [T = int, U = int] compiled with gcc HEAD 8.0.1 201803 but fails to compile with clang HEAD 7.0.0 unless angle brackets are used during instantiation:
S s; s.f(); // error: declaration of variable 's' with deduced type 'S' requires an initializer
S<> t; t.f(); // Correct
This issue aside, I've checked the other template flavors for this particular behavior and the code is accepted or rejected in a pretty irregular manner:
Template function
template <typename T, typename U = int> void function();
template <typename T = int, typename U> void function()
{ std::cout << __PRETTY_FUNCTION__ << '\n'; }
int main()
{
/* Rejected by GCC: no matching function for call to 'function()'
template argument deduction/substitution failed:
couldn't deduce template parameter 'T'
same error with function<>()
CLang compiles without issues */
function(); // CLang prints 'void function() [T = int, U = int]'
return 0;
}
Template variable
template <typename T, typename U = int> int variable;
template <typename T = int, typename U> int variable = 0;
int main()
{
/* GCC complains about wrong number of template arguments (0, should be at least 1)
while CLang complains about redefinition of 'variable' */
std::cout << variable<> << '\n';
return 0;
}
Template alias
template <typename T, typename U = int> using alias = int;
template <typename T = int, typename U> using alias = int;
int main()
{
/* GCC complains about redefinition of 'alias'
while CLang compiles just fine. */
alias<> v = 0;
std::cout << v << '\n';
return 0;
}
The standards text about this feature doesn't tell apart the different template types so I thought that they should behave the same.
But yet, the template variable case is the one rejected by both compilers, so I have some doubts about the template variable option. It makes sense to me that CLang is right rejecting the template variable complaining about redefinition while GCC is wrong by rejecting the code for the wrong reasons, but this reasoning doesn't follow what the standard says in [temp.param]/10.
So what should I expect for the case of template variables?:
Code rejected due to redefinition (CLang is right).
Code accepted, merging both template definitions (Both GCC and CLang are wrong).
For class template argument deduction, this is a clang bug. From [temp.param]/14:
The set of default template-arguments available for use is obtained by merging the default arguments from all prior declarations of the template in the same way default function arguments are ([dcl.fct.default]). [ Example:
template<class T1, class T2 = int> class A;
template<class T1 = int, class T2> class A;
is equivalent to
template<class T1 = int, class T2 = int> class A;
— end example ]
When you write S s, both template arguments are defaulted, and so the rewrite of the default constructor is:
template <typename T=int, typename U=int>
S<T, U> __f();
Which is viable and should deduce S<int, int>.
For functions, you do not need to specify <> to call a function template if all the template parameters can be deduced. This is [temp.arg.explicit]/3:
If all of the template arguments can be deduced, they may all be omitted; in this case, the empty template argument list <> itself may also be omitted.
Note that this applies to deduction. There is no deduction for alias templates or variable templates. Hence, you cannot omit the <>. This is [temp.arg]/4:
When template argument packs or default template-arguments are used, a template-argument list can be empty. In that case the empty <> brackets shall still be used as the template-argument-list.
Disclaimer: the following is valid in context of C++14. With C++17 both compilers are wrong. See the another answer by Barry.
Looking into details I see that Clang is correct here, while GCC is confused.
First case, class template (unlike function template) indeed requires <>.
Second case, function template, is treated by Clang exactly like the first case, without the syntactic requirement to use <> to indicate that template is used. This applies in C++ to function templates in all contexts.
Third case: as of variables, I see that Clang is correct while GCC is confused. If you redeclare the variable with extern Clang accepts it.
template <typename T, typename U = int> int variable = 0;
template <typename T = int, typename U> extern int variable;
int main()
{
// accepted by clang++-3.9 -std=c++14
std::cout << variable<> << '\n';
return 0;
}
so it again behaves consistently both with standard and the previous cases. Without extern this is redefinition and it is forbidden.
Fourth case, using template. Clang again behaves consistently. I used typeid to ensure that alias is indeed int:
template <typename T, typename U = int> using alias = int;
template <typename T = int, typename U> using alias = int;
int main()
{
alias<> v = 0;
std::cout << v << '\n';
std::cout << typeid(v).name() << '\n';
return 0;
}
then
$ ./a.out | c++filt -t
outputs
0
int
So Clang indeed makes no difference of what kind of template is re-declared, as stated in the standard.
For some reason, I can see the top most template<typename T> X<...>::fn(T&&), but not the base class versions. Importing them using the using keyword doesn't work. As I understand it:
In Class definition
... If the derived class already has a member with the same name, parameter list, and qualifications, the derived class member hides or overrides (doesn't conflict with) the member that is introduced from the base class.
Sample code:
#include <iostream>
#include <type_traits>
#define ENABLE_IF(...) std::enable_if_t<__VA_ARGS__, int> = 0
struct dummy {};
template<typename T>
struct always_false : std::false_type {};
template<typename...Ts>
struct X;
template<typename Tx, typename T, typename...Ts>
struct X<Tx, T, Ts...> : X<Tx, Ts...>
{
using X<Tx, Ts...>::fn;
template<typename R, ENABLE_IF(std::is_same<T, R>::value)>
auto fn(R&& x)
{
return x;
}
};
template<typename Tx>
struct X<Tx>
{
template<typename R>
auto fn(R&& x)
{
static_assert(always_false<R>::value, "Invalid type");
}
};
int main()
{
X<dummy, int, float> x;
std::cout << x.fn(1) << std::endl;
std::cout << x.fn(1.f) << std::endl;
std::cout << "Hello, world!\n";
}
I've tried this on g++, clang and VC++ and they all have various errors, (ambiguous call, member function disabled and could not deduce). It's interesting to note that g++ is failing on the call to X<dummy, int, float>::fn(int&&), whereas clang and VC++ are failing on calling X<dummy, int, float>::fn(float&&).
As I understand it, the compiler should ignore the absolute base class member function template<typename R> R X<dummy>::fn(R&&) when calling X<dummy, int, float>::fn(float&&), because that template should resolve to float X<dummy>::fn(float&&) which is an exact match to derived member function float X<dummy, float>::fn(float&&) requiring the derived one to be called without ambiguity.
What am I doing wrong? What am I not understanding?
Edit
To paraphrase T.C.'s answer so far, "this is what the spec says", which I would say is not the correct interpretation. The two points given there are in conflict with each other. If they are an equally good match (point 1), then only the most derived function signature should be visible (point 2).
Anyway, if the problem is a spec problem, then it should go away if I were to disable the possibility of a matching overload which would result in an ambiguity. Thus the following should work:
#include <iostream>
#include <type_traits>
#define ENABLE_IF(...) std::enable_if_t<__VA_ARGS__, int> = 0
template<typename T>
struct always_false : std::false_type {};
template<typename...Ts>
struct list {};
template<typename...Ts>
struct is_one_of;
template<template <typename...> class TT, typename T, typename T1, typename...Ts>
struct is_one_of<T, TT<T1, Ts...>> : is_one_of<T, TT<Ts...>> {};
template<template <typename...> class TT, typename T, typename...Ts>
struct is_one_of<T, TT<T, Ts...>> : std::true_type {};
template<template <typename...> class TT, typename T>
struct is_one_of<T, TT<>> : std::false_type {};
template<typename...Ts>
struct X;
template<typename L, typename T, typename...Ts>
struct X<L, T, Ts...> : X<L, Ts...>
{
using X<L, Ts...>::fn;
template<typename R, ENABLE_IF(std::is_same<T, R>::value)>
constexpr auto fn(R&& x) const
{
return x;
}
};
template<typename L>
struct X<L>
{
template<typename R, ENABLE_IF(!is_one_of<R, L>::value)>
constexpr auto fn(R&& x) const
{
static_assert(always_false<R>::value, "Type R didn't match");
}
};
template<typename...Ts>
struct XX : X<list<Ts...>, Ts...> {};
int main()
{
XX<int, float> x;
std::cout << x.fn(1) << std::endl;
std::cout << x.fn(2.f) << std::endl;
}
It does work under g++, but has the same problems under clang and VC++. So, is g++ the only one that is conforming here and the rest are defective?
Two things:
Your baseline catch-all version of fn is an equally good match as the other fn varieties; therefore at best you'll get an ambiguous overload error.
Hiding for using-declarations does not consider the full signature (which for function templates would include the template parameter list). It only considers 1) name, 2) (function) parameter-type-list, 3) cv-qualification, and 4) ref-qualifier (if any). If all four match, the base class function template is hidden and not brought in by the using-declaration. Notably, the template parameter list is not considered. In your case, the only thing different between the various fns is the template parameter list; they all have the same name, same parameter-type-list, same cv-qualification, and same ref-qualifier (or lack thereof). Therefore, the most derived fn will hide all fns from the base classes.
GCC appears to not implement this part to spec and consider the template parameter list when deciding on hiding.
One possible fix for this part is to move the enable_if to a function parameter, which is considered by the hiding check.
The way overload resolution in C++ works is:
Name lookup to build a set of candidate functions and function templates.
For each candidate function template, perform template argument deduction. If deduction fails, remove it from the overload set. If deduction succeeds, replace the candidate function template with the deduced specialization.
Eliminate non-viable candidates from the set of candidates.
If the set is empty, error. Otherwise, find the best viable function in the candidates.
Hiding operates at the first step: if a declaration is hidden, it is not found by name lookup, and therefore it is not in the initial set of candidates and will not be considered by overload resolution under any circumstance, regardless of what happens in step 2, 3, or 4. A hidden declaration effectively doesn't exist for the purposes of overload resolution.
So, in your case, the base class fns are all hidden. What does it mean? It means that the only candidate found by name lookup is the int one from the most derived class, nothing else. If template argument deduction and substitution succeed, that function template will be called. If they fail (as in the x.fn(2.f) case), then there is no viable candidate left, and you get an error.
I have this code:
struct My
{
typedef int foo;
};
struct My2
{
};
template <typename T>
void Bar(const T&, int z = typename T::foo())
{
std::cout << "My" << std::endl;
}
void Bar(...)
{
std::cout << "..." << std::endl;
}
int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Compile error: no type named ‘foo’ in ‘struct My2’
return 0;
}
I suppose, that if some class T doesn't have typedef foo inside, compiler should exclude first overload and choose overload with ellipsis. But I check this code on MSVC, gcc and clang and I get compile error on those compilers. Why SFINAE doesn't work in this case?
The type of z is not subject to template substitution, it is always int. This means there is no opportunity for SFINAE, and you instead get a compiler error when attempting to resolve T::foo for the default value. Default arguments do not participate in overload resolution, instead being instantiated only when missing from the function call. Section 14.7.1 (paragraphs 13/14) of the standard describes this behaviour, but does not give justification for the lack of SFINAE here.
SFINAE can be allowed to happen by making the type of z a template parameter, as below:
(live example: http://ideone.com/JynMye)
#include <iostream>
struct My
{
typedef int foo;
};
struct My2
{
};
template<typename T, typename I=typename T::foo> void Bar(const T&, I z = I())
{
std::cout << "My\n";
}
void Bar(...)
{
std::cout << "...\n";
}
int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Also OK
return 0;
}
This will use the "My" version for the first call, and the "..." version for the second call. The output is
My
...
However, if void Bar(...) was a template, for whatever reason, the "My" version will never get a chance:
(live example: http://ideone.com/xBQiIh)
#include <iostream>
struct My
{
typedef int foo;
};
struct My2
{
};
template<typename T, typename I=typename T::foo> void Bar(const T&, I z = I())
{
std::cout << "My\n";
}
template<typename T> void Bar(T&)
{
std::cout << "...\n";
}
int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Also OK
return 0;
}
Here, the "..." version is called in both cases. The output is:
...
...
One solution is to use class template (partial) specialisation; provide the "..." version as the base, with the type of the second parameter defaulted to int, and the "My" version as a specialisation where the second parameter is typename T::foo. In conjunction with a plain template function to deduce T and dispatch to the appropriate class' member function, this produces the desired effect:
(live example: http://ideone.com/FanLPc)
#include <iostream>
struct My
{
typedef int foo;
};
struct My2
{
};
template<typename T, typename I=int> struct call_traits {
static void Bar(...)
{
std::cout << "...\n";
}
};
template<typename T> struct call_traits<T, typename T::foo> {
static void Bar(const T&, int z=typename T::foo())
{
std::cout << "My\n";
}
};
template<typename T> void Bar(const T& t)
{
call_traits<T>::Bar(t);
}
int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Still OK
return 0;
}
Here, the output is:
My
...
The type z is an int, is not being deduced by the compiler, no room for SFINAE to take place. The value being used to initialise z is based on the default of T::foo, which doesn't exist; hence the error.
If the type for z is elevated to the template itself, substitution can now fail, and SFINAE kicks in.
#include <iostream>
struct My
{
typedef int foo;
};
struct My2
{
};
template <typename T, typename I = typename T::foo>
void Bar(const T&, I z = I())
{
(void)z; // silence any warnings on unused
std::cout << "My" << std::endl;
}
void Bar(...)
{
std::cout << "..." << std::endl;
}
int main()
{
My my;
Bar(my);
My2 my2;
Bar(my2); // Compiles
return 0;
}
Live sample
In order for a function template to be part of the overloaded list of candidate functions, the template argument deduction must succeed. If it fails, then the candidate is removed from the list. Hence, if no deduction failure occurs, it is added to the candidate list (but this does not preclude further errors if it is finally selected).
14.8.3/1 Overload resolution
A function template can be overloaded either by (non-template) functions of its name or by (other) function templates of the same name. When a call to that name is written (explicitly, or implicitly using the operator notation), template argument deduction (14.8.2) and checking of any explicit template arguments (14.3) are performed for each function template to find the template argument values (if any) that can be used with that function template to instantiate a function template specialization that can be invoked with the call arguments. For each function template, if the argument deduction and checking succeeds, the template arguments (deduced and/or explicit) are used to synthesize the declaration of a single function template specialization which is added to the candidate functions set to be used in overload resolution. If, for a given function template, argument deduction fails, no such function is added to the set of candidate functions for that template. The complete set of candidate functions includes all the synthesized declarations and all of the non-template overloaded functions of the same name. The synthesized declarations are treated like any other functions in the remainder of overload resolution, except as explicitly noted in 13.3.3.
Template argument deduction is performed on the function type and its template arguments themselves.
14.8.2/8 Template argument deduction
If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguments. [ Note: If no diagnostic is required, the program is still ill-formed. Access checking is done as part of the substitution process. —end note ] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure.
From the OP, the function Bar<T> is added to the candidate list since it can be deduced what the type for T is. It is instantiated and the default arguments are checked, and hence it fails.
14.7.1/13 Implicit instantiation
If a function template f is called in a way that requires a default argument to be used, the dependent names are looked up, the semantics constraints are checked, and the instantiation of any template used in the default argument is done as if the default argument had been an initializer used in a function template specialization with the same scope, the same template parameters and the same access as that of the function template f used at that point. This analysis is called default argument instantiation. The instantiated default argument is then used as the argument of f.
Quotes taken from draft n3797
One more C++03 compatible option for you. Because in answers above default argument was used in template function and it is not permitted in standard.
#include <iostream>
struct TypeWithFoo{
typedef int Foo;
};
template<typename T, bool>
struct onFooAction;
template<typename T>
struct onFooAction<T, false>{
void operator ()(const T &t){
std::cout << "No foo :(\n";
}
};
template<typename T>
struct onFooAction<T, true>{
void operator ()(const T &t){
std::cout << "Foo =)\n";
}
};
template<typename T>
struct hasFoo{
typedef char yes[1];
typedef char no[2];
template<typename C>
static yes& testForFoo(typename C::Foo*);
template<typename>
static no& testForFoo(...);
static const bool value = sizeof(testForFoo<T>(0)) == sizeof(yes);
};
template<typename T>
void bar(const T &t){
onFooAction<T, hasFoo<T>::value>()(t);
}
int main(){
bar(10);
bar(TypeWithFoo());
}