How can I make forbidden template instantiations clearly visible at compile time? I was thinking about throwing an exception by inheriting it:
template<string message> struct exception {};
template<class T> struct non_void;
template<class T> using non_void_t = typename non_void<T>::type;
template<> struct non_void<void>: exception<string{"non_void: argument is void"}> {};
non_void_t<void> poorly_typed_object;
but gcc 10 simply complains aboue some error:
$ g++ -std=c++20 exc.cpp
exc.cpp: In substitution of ‘template<class T> using non_void_t = typename non_void::type [with T = void]’:
exc.cpp:11:16: required from here
exc.cpp:7:25: error: no type named ‘type’ in ‘struct non_void<void>’
7 | template<class T> using non_void_t = typename non_void<T>::type;
and does not display anything about the erroneus instantiation's parent type, so the actual error message never appears in compiler's output.
Is there a way to make it more apparent?
I would suggest making the failing specialization non_void_t<void> a constrained partial specialization that triggers a static_assert on instantiation:
#include <type_traits>
template<class T> concept Void = std::is_void_v<T>;
template<Void T> struct non_void<T> { static_assert(!std::is_void_v<T>, "argument is void"); };
Example.
Related
I'm trying to add a default "hidden" setting into a templated class:
template<bool DebugMode=false, typename... Args>
struct A
{
A() {};
};
int main()
{
A<double, double> a;
}
which fails when compile with g++ 8.3.1 and C++17:
error: type/value mismatch at argument 1 in template parameter list for ‘template<bool DebugMode, class ... Args> struct A’
note: expected a constant of type ‘bool’, got ‘double’
Yet I don't understand why g++ can't do any pattern matching in template arguments. Will it be fixed a newer C++ version ?
It's basically the same as with default function arguments: You can only omit parameters from the right. And I don't expect this to change, also because what you want to do can be achieved by adding a layer of indirection:
template<bool DebugMode=false>
struct Wrap {
template <typename ...T> struct A {};
};
template <typename...T> using A = Wrap<>::A<T...>;
int main() {
A<double, double> a;
}
Alternatively:
template <bool DebugMode=false,typename ...T>
struct A_impl {};
template <typename...T>
using A = A_impl<false,T...>;
Though here the default false cannot be really used, for the using you still have to specify it.
I want to use the standard code to write the utils like std::is_union,we know class type can not extends union type,it's error,so some code like these
#include <iostream>
template<typename T>
class class_type_can_extends :public T{
public:
using type = void;
};
template<typename T,typename U = void>
struct is_not_union:std::false_type {
};
template<typename T>
struct is_not_union < T, std::void_t<typename class_type_can_extends <T>::type >> :std::true_type {
};
class c_data{
};
union u_data{
};
int main(){
/*#1*/ std::cout<< is_not_union<c_data>::value<<std::endl; /*print true*/
/*#2*/ std::cout<< is_not_union<u_data>::value<<std::endl; /*this code make
all complier error*/
}
g++ print error:
main.cpp: In instantiation of ‘class class_type_can_extends<u_data>’:
main.cpp:26:43: recursively required by substitution of ‘template<class T> struct is_not_union<T, std::void_t<typename class_type_can_extends<T>::type> > [with T = u_data]’
main.cpp:26:43: required from here
main.cpp:3:7: error: base type ‘u_data’ fails to be a struct or class type
class class_type_can_extends :public T {
clang print error:
main.cpp:3:38: error: unions cannot be base classes
class class_type_can_extends :public T {
~~~~~~~^
main.cpp:14:47: note: in instantiation of template class 'class_type_can_extends<u_data>' requested here
struct is_not_union < T, std::void_t<typename class_type_can_extends <T>::type >> :std::true_type {
^
main.cpp:26:23: note: during template argument deduction for class template partial specialization 'is_not_union<T,
std::void_t<typename class_type_can_extends<T>::type> >' [with T = u_data]
/*#2*/ std::cout << is_not_union<u_data>::value << std::endl; /*this code make
^
main.cpp:26:23: note: in instantiation of template class 'is_not_union<u_data, void>' requested here
1 error generated.
vs:
error C2569
why #2 code make complier error,The complier would be using SFINAE rules on #2 code(substituted T by "u_data" ,then Failed ),and to chose primary template?why the sfinae not effective here,may be a bug here?
From cppreference:
Only the failures in the types and expressions in the immediate context of the function type or its template parameter types or its explicit specifier (since C++20) are SFINAE errors. If the evaluation of a substituted type/expression causes a side-effect such as instantiation of some template specialization, generation of an implicitly-defined member function, etc, errors in those side-effects are treated as hard errors
SFINAE applies on immediate context, here you have a hard error failure.
In typename class_type_can_extends<T>::type, SFINAE applies if type doesn't exist, not if instantiation of class_type_can_extends<T> fails.
Notice that we cannot distinguish between union and class types using only standard C++
(without std::is_union). Most compilers provide intrinsics for that.
I encountered a very strange compiler error. For some reason the posted code does compile properly with g++ (7.3.0) while clang (7.0.0) fails:
../TemplateAlias/main.cpp:64:9: error: no matching function for call to 'freeFunc'
freeFunc(new Func, dummyField);
^~~~~~~~
../TemplateAlias/main.cpp:73:12: note: in instantiation of member function 'Helper<Traits<double, ConcreteData, ConcreteField> >::func' requested here
helper.func();
^
../TemplateAlias/main.cpp:21:13: note: candidate template ignored: deduced conflicting templates for parameter '' ('FieldData' vs. 'ConcreteData')
static void freeFunc(SomeFunc<T, FieldData>* func,
^
Both compiler options were set to -std=c++14
template<typename T>
struct ConcreteData
{
T data;
};
template<typename T, template<typename U> class FieldData>
struct ConcreteField
{
FieldData<T> someMember;
};
template<typename T, template<typename U> class FieldData>
struct SomeFunc
{
};
template<typename T, template<typename U> class FieldData>
static void freeFunc(SomeFunc<T, FieldData>* func,
ConcreteField<T, FieldData>& field)
{
// apply the func on data
(void)field; // silence compiler warning
delete func;
}
template<
typename ScalarType,
template<typename U> class FieldDataType,
template<typename U, template <typename X> class Data> class FieldType
>
struct Traits
{
using Scalar = ScalarType;
template<typename T>
using FieldData = FieldDataType<T>;
using Field = FieldType<Scalar, FieldDataType>; // fails with clang only
// using Field = FieldType<Scalar, FieldData>; // using this line helps clang
};
template<typename Traits>
struct Helper
{
// alias all types given by trait for easier access
using Scalar = typename Traits::Scalar;
using Field = typename Traits::Field;
template<typename U>
using DataAlias = typename Traits::template FieldData<U>;
void func()
{
// using Func = SomeFunc<Scalar, DataAlias>; // this line is intended, but fails with both GCC and clang
using Func = SomeFunc<Scalar, Traits::template FieldData>; // compiles only with GCC, fails with clang
Field dummyField;
freeFunc(new Func, dummyField);
}
};
int main()
{
using ConcreteTraits = Traits<double, ConcreteData, ConcreteField>;
Helper<ConcreteTraits> helper;
helper.func();
return 0;
}
According to cppreference.com:
A type alias declaration introduces a name which can be used as a
synonym for the type denoted by type-id. It does not introduce a new
type and it cannot change the meaning of an existing type name. There
is no difference between a type alias declaration and typedef
declaration. This declaration may appear in block scope, class scope,
or namespace scope.
and
Alias templates are never deduced by template argument deduction when
deducing a template template parameter.
In my understanding both types (ConcreteData and FieldData) should be equivalent. Why is clang failing in this condition and why do both compiler fail when using the "second stage" alias? Which compiler is right according to the C++ standard? Is it a compiler bug or a subtle ambiguous interpretation of the C++14 standard?
Borrowing the minimal example of #Oktalist.
template <typename>
class T {};
template <typename _>
using U = T<_>;
template <template <typename> class X>
void f(A<X>, A<X>) {}
if you replace f by:
template <template <typename> class X, template <typename> class Y>
void f(A<X>, A<Y>) {}
the code no longer fail to compile. You can see that the problem is about equivalence of template parameters X and Y, they are deduced to different types.
The equivalence of types produced by alias template are only considered when referring to specialization of the alias, as is specified on [temp.alias]/2:
When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template
Using this rule and the rules for equivalence [temp.type]/1:
T<int> and U<int> are equivalent, so are X<T<int>> and Z<U<int>>, but this rule doesn't extend to the alias template U being equivalent to the class template T (by themselves, they aren't specializations).
This is the same scenario for the alias FieldData and the class template ConcreteData.
There are in fact two defect report, CWG-1286 and CWG-1244 that propose the equivalence extension for alias templates.
I try to understand meaning and implications of 14.5.5/8 of the C++11 standard (idem in C++14 and, I suppose, in C++17)
The type of a template parameter corresponding to a specialized non-type argument shall not be dependent on a parameter of the specialization.
and, as usual, to understand who's correct between g++ and clang++.
The standard show the following example
template <class T, T t> struct C {};
template <class T> struct C<T, 1>; // error
and both g++ and clang++ give error.
So far, so good.
Let's complicate a little the example adding a type
template <typename, typename T, T>
struct foo { };
template <typename T>
struct foo<T, int, 1> { }; // compile
template <typename T>
struct foo<T, T, 1> { }; // error
Both g++ and clang++ compile the first partial specialization (the type of 1, int, isn't a parameter of the specialization) and give error with the second one (the type of 1 is T, a parameter of the specialization)
So far, so good.
Let's introduce a template struct bar with an internal type that doesn't depend from the template parameter
template <typename>
struct bar
{ using type = int; };
and the following program
template <typename>
struct bar { using type = int; };
template <typename, typename T, T>
struct foo { };
template <typename T>
struct foo<T, typename bar<T>::type, 1> { };
int main ()
{ }
it's compiled without error by g++ (tried in wandbox with 4.9.3, 5.5.0, 7.2.0 and head 8.0.0; with c++11, c++14 and, when available, c++17) but clang++ (3.9.1, 4.0.1, 5.0.0, head 6.0.0; c++11, c++14, c++17) give the following error
prog.cc:11:38: error: non-type template argument specializes a template parameter with dependent type 'T'
struct foo<T, typename bar<T>::type, 1> { };
^
prog.cc:7:34: note: template parameter is declared here
template <typename, typename T, T>
~^
As usual: who's right?
clang++, that consider 1 dependent on T (when typename bar<T>::type is fixed as int) or g++ that doesn't relieve this dependencies?
For completeness I have to say that changing bar as follows
template <typename T>
struct bar { using type = T; };
so making the bar<T>::type dependant on T, nothing change: g++ compile without error, and clang++ give the same error.
Look at it from the compiler's point of view.
template <class T, T t> struct C {};
template <class T> struct C<T, 1>; // error
For the specialization, the compiler doesn't know if T can indeed have a value of 1, and so the specialization is invalid.
For
template <typename T>
struct foo<T, typename bar<T>::type, 1> { };
Who's saying that type is always int? You might think that it is obvious, but I could introduce a specialization of bar for one specific T so that type is a std::string:
template<>
struct bar<const volatile int> { using type = std::string };
Basically, your statement "when typename bar<T>::type is fixed as int" is wrong, it is not fixed.
Now what? The standard here says the same thing as for your first example, the specialization is ill-formed, because as your quote correctly states, the type of the non-type parameter depends on another (templated) type of the specialization, namely T, which is unknown. In that regard, clang is right, and gcc is wrong.
I have a templated class for which I want to enable different constructors depending on the template parameter. Specifically, I want to use std::is_compound as a criterion.
SSCCE
// bla.cpp
#include <type_traits>
#include <vector>
template <typename T>
class Foo{
double bar;
public:
template<typename U=T,
class = typename std::enable_if<std::is_compound<U>::value>::type
>
Foo(typename T::value_type& b):bar(b){}
template<typename U=T,
class = typename std::enable_if<!std::is_compound<U>::value>::type
>
Foo(T b):bar(b){}
};
int main(){
double d=1.0;
Foo<std::vector<double>> f2(d); // works
Foo<double> f1(d); // compiler error
}
I get the following compilation error:
g++ -std=gnu++11 bla.cpp
bla.cpp: In instantiation of ‘class
Foo<double>’: bla.cpp:22:18: required from here bla.cpp:11:2: error:
‘double’ is not a class, struct, or union type
coliru
The problem seems to be that the first version of the constructor is being used, which fails because double::value_type does not exist. The problem is that that constructor shouldn't be in Foo<double> in the first place, because std::is_compound<double>::value is false.
Why does std::enable_if seem to be not working properly?
In typename T::value_type&, T cannot be substituted by double. Since T is a template parameter of the class template and not of the constructor template, you cannot get the overload excluded by substitution failure. SFINAE only works if parameters of the template in question are involved.
If you use typename U::value_type&, you get a substitution failure that is not an error as U is a parameter of the constructor template, not of the class template.