I'm toying with SFINAE and I try to check my Inputs is made of Input of various types. The error provided by clang doesn't help much. Do you have any idea ?
Thanks
struct IsFree
{
};
template <typename _Type, typename _State>
struct Input
{
};
template <typename... _Inputs>
struct Inputs
{
};
template <template <typename _Type, typename _State> class, typename... _Inputs>
struct Inputs<Input<_Type, _State>, _Inputs...> : public Inputs<_Inputs...>
{
};
Somewhere else :
auto temp = Inputs<Input<float, IsFree>, Input<float, IsFree>> {};
I get using clang-5.0 and -std=c++17 :
13 : <source>:13:21: error: use of undeclared identifier '_Type'
struct Inputs<Input<_Type, _State>, _Inputs...> : public Inputs<_Inputs...>
^
13 : <source>:13:35: error: expected a type
struct Inputs<Input<_Type, _State>, _Inputs...> : public Inputs<_Inputs...>
^
2 errors generated.
Compiler exited with result code 1
template <template <typename _Type, typename _State> class, typename... _Inputs>
struct Inputs<Input<_Type, _State>, _Inputs...> : public Inputs<_Inputs...>
{
};
needs to be
template <typename _Type, typename _State, typename... _Inputs>
struct Inputs<Input<_Type, _State>, _Inputs...> : public Inputs<_Inputs...>
{
};
in the pattern Input<_Type, _State> _Type and _State are just type wildcards, you only need the template <typename, typename> class F template template parameter syntax if you need to match a template template parameter with a wild card. In this case you are matching the template with a known template named Input
Your partial specialization for the last case is not correct. You need to deduce _Type and _State, not have a template template parameter.
template <class _Type, class _State, typename... _Inputs>
struct Inputs<Input<_Type, _State>, _Inputs...> : public Inputs<_Inputs...>
{
};
In your original code, the names inside the template template parameter do not introduce template parameters for that partial specialization.
Also note that names beginning with an underscore and capital letter are reserved to the implementation, so you should not use them in your own code.
Other than the perfect answers already provided by others, may I ask what your expected outcome is of a type like Inputs<Input<float, IsFree>, int, Input<int, IsFree>>? Notice the stray int in the middle. Would you like the base class recursion to stop at the first non-Input<> argument, as currently is the case in your example, or rather have a compile error?
If the latter, might I suggest changing the general Inputs template definition to be an incomplete type, and then specializing for an empty struct in case of zero template arguments, like so:
// General case, incomplete type
template<class... T>
struct Inputs;
// Special case for zero arguments, empty struct
template<>
struct Inputs<>
{
};
Together with your specialization for Inputs<Input<*,*>, ...>, this will make sure you can never instantiate a template using arguments that are not a list of Input<*,*>.
Related
Here is some code that compiles in GCC (on godbolt at least - can't test locally), for handling a compile-time dependency system - the conversion operator here is to make it easier to take an entity that specifies what it can read/write and reduce it down implicitly to a more restricted form when passing into functions (please pretend that the operator below does some static_assert kind of enforcement).
template<typename... Args>
struct WriteList{};
template<typename... Args>
struct ReadList{};
template<typename Reads = ReadList<>, typename Writes = WriteList<>>
class TypedEntity;
template <typename... ReadTypes, template <typename...> typename Reads, typename... WriteTypes, template <typename...> typename Writes>
class TypedEntity<Reads<ReadTypes...>, Writes<WriteTypes...>>
{
public:
template <typename... OtherReadTypes, typename... OtherWriteTypes>
operator TypedEntity<ReadList<OtherReadTypes...>, WriteList<OtherWriteTypes...>>()
{
return {};
}
};
struct ComponentA{};
struct ComponentB{};
struct ComponentC{};
void TestFunc1(TypedEntity<ReadList<ComponentA, ComponentB>, WriteList<ComponentC>> entity)
{
}
void TestFunc2(TypedEntity<ReadList<ComponentA>, WriteList<>> entity)
{
}
void TestFunc3(TypedEntity<ReadList<ComponentA>, WriteList<ComponentC>> entity)
{
}
int main()
{
TypedEntity<ReadList<ComponentA, ComponentB>, WriteList<ComponentB>> entity;
TestFunc1(entity);
TestFunc2(entity);
TestFunc3(entity);
return 0;
}
But under MSVC (latest, i.e.g 19.28, as well as some other 19.x versions I've sampled (19.14, 19.24, etc)):
error C3547: template parameter 'OtherWriteTypes' cannot be used because it follows a template parameter pack and cannot be deduced from the function parameters of 'TypedEntity<Reads<ReadTypes...>,Writes<WriteTypes...>>::operator TypedEntity<ReadList<OtherReadTypes...>,WriteList<OtherWriteTypes...>>'
Is this valid C++ and MSVC is wrong?
Is there a workaround for this issue in MSVC?
Appreciate it.
The error message hints at MSVC's mistake (emphasis mine):
error C3547: template parameter 'OtherWriteTypes' cannot be used because it follows a template parameter pack and cannot be deduced from the function parameters of 'TypedEntity<Reads<ReadTypes...>,Writes<WriteTypes...>>::operator TypedEntity<ReadList<OtherReadTypes...>,WriteList<OtherWriteTypes...>>'
It's true it can't be deduced from the function parameters - but for a conversion function, deduction happens from the return type, not the empty parameter list.
So a workaround is to simplify the template signature of the conversion function. Presuming the implementation needs to know the actual ReadTypes... and WriteTypes..., the operator() definition can just call a private ordinary member function which can deduce them from parameters.
template<typename T>
struct is_WriteList_s : public std::false_type {};
template<typename... Args>
struct is_WriteList_s<WriteList<Args...>> : public std::true_type {};
template<typename T>
concept is_WriteList = is_WriteList_s<T>::value;
template<typename T>
struct is_ReadList_s : public std::false_type {};
template<typename... Args>
struct is_ReadList_s<ReadList<Args...>> : public std::true_type {};
template<typename T>
concept is_ReadList = is_ReadList_s<T>::value;
template <typename... ReadTypes, template <typename...> typename Reads,
typename... WriteTypes, template <typename...> typename Writes>
class TypedEntity<Reads<ReadTypes...>, Writes<WriteTypes...>>
{
private:
template <typename... OtherReadTypes, typename... OtherWriteTypes>
TypedEntity<ReadList<OtherReadTypes...>, WriteList<OtherWriteTypes...>>
convert_impl(std::type_identity<TypedEntity<
ReadList<OtherReadTypes...>, WriteList<OtherWriteTypes...>>>) const;
public:
template <is_ReadList OtherReadList, is_WriteList OtherWriteList>
operator TypedEntity<OtherReadList, OtherWriteList>() const
{
return convert_impl(
std::type_identity<TypedEntity<OtherReadList, OtherWriteList>>{});
}
};
I used std::type_identity just as a type-wrapper which doesn't actually have any data members or logic. If not compiling with C++20 support, any dummy template struct would do, or a raw pointer with null argument.
If using a version of MSVC or a /std: switch which does not support concepts, the concepts can be converted to SFINAE tricks or simple static_asserts.
A class WithTTMember has a template member type named TT.
struct WithTTMember {
template<typename> using TT = void;
};
Another class ExpectTT takes a template template parameter:
template< template<typename> typename TT >
struct ExpectTT {};
ExpectTT<WithTTMember::TT> can be successfully instantiated.
A third class ExpectTWithTT expects a template parameter with a template member type named TT, and instantiates ExpectTT using it:
template<typename T>
struct ExpectTWithTT {
using X = ExpectTT<typename T::TT>; // this doesn't compile
};
I expect ExpectTWithTT<WithTTMember>::X to be the same type as ExpectTT<WithTTMember::TT>. However the code above is fails to compile.
I tried injecting the faulty line with a combination of template and typename keywords following compiler messages and my instinct, but I couldn't get it to work.
How can I express what I want?
Any C++ version is fine.
You should use template keyword to tell that T::TT is a template.
template<typename T>
struct ExpectTWithTT {
using X = ExpectTT<T::template TT>;
// ^^^^^^^^
};
I'm trying to compile following code :
#include <iostream>
template<class T> struct Container1;//forward declaration
template<class T> struct Container2;//forward declaration
template<class Container,class Type>
using _Make_Container=typename Container<Type>;
template<class T>
struct Container1
{};
template<class T>
struct Container2
{};
int main()
{
_Make_Container<Container1,int> a;
}
Which give me some errors:
expected nested-name-specifier before 'Container'
using _Make_Container=typename Container<Type>;
^
error: 'Container' is not a template
It seems OK to me because we send Container1 and int to using Then it becomes :
using _Make_Container=typename Container1<int>;
//then
Container1<int> a;
I can't understand the reason for that errors !! Any idea?
Is there any way for making that works ?
Those error messages are pretty clear.
expected nested-name-specifier before 'Container'
using _Make_Container=typename Container<Type>;
^
You're not referring to a nested dependent type so there's no need for the typename keyword here. Read this for a detailed explanation on where typename is required.
error: 'Container' is not a template
Just like it says, your alias template doesn't indicate Container is a template, but then it proceeds to use it as one. You need to use a template template parameter for Container
template<template<typename> class Container,class Type>
using _Make_Container = Container<Type>;
And borrowing Yakk's excellent suggestion, you should change the alias template definition to
template<template <typename...> class T1, typename... T2>
using Make_Container = T1<T2...>;
This lets you do the following:
template<class T, class T1>
struct Container2
{};
Make_Container<Container2,int,long> b;
Finally, names using a leading underscore followed by an upper case letter are reserved by the implementation. So you should rename _Make_Container to Make_Container.
You have to write the alias template like this
template<template <typename> class T1,class T2>
using _Make_Container= T1<T2>;
template< template <class> class Container,class Type>
using _Make_Container = Container<Type>;
I wrote an abstract container template class that should define numeric operators (unary + and -, binary +, - and *) if it make sens for the template parameter (that is, if it is a numeric type).
Then, I would like to apply those numeric operations on containers of containers of numeric values (and on containers of containers of containers of numeric values, and so on).
I wrote the following code. The (A) marker shows how I tried to solve the recursive specialization problem.
template <typename T>
struct is_numeric : public std::is_arithmetic<T>{};
template <typename T> /* (A) */
struct is_numeric<GenericContainer<T>> : public std::is_arithmetic<T>{};
/* Classic generic container for non-numeric base types */
template <typename T, bool isNumeric=false>
class BaseContainer : public GenericContainer<T> {};
/* Numeric container: +,-,* operations for numeric base types */
template <typename T>
class BaseContainer<T, true> : public NumericContainer<T> {};
/* Arithmetic base types should map on numeric containers */
template <typename T>
class Container : public BaseContainer<T, is_numeric<T>::value> {};
Then, in a test program, I have the following assertions:
/* Vector inherits from Container */
typedef Vector<int, 3> V3D;
ASSERT(is_numeric<int>::value); /* # => OK */
ASSERT(is_numeric<double>::value); /* # => OK */
ASSERT(is_numeric<V3D>::value); /* # => FAIL */
The two firsts assertions work as expected
Your solution fails for a very specific reason: a template type parameter specialization will match only the exact type, and not any derived type.
If you wish for derived types to also match, you need switch gears and use another strategy. In the age of constexpr switching to functions will let you use overloading resolution to your advantage (as one strategy among others):
// Basis
constexpr bool is_numeric_impl(...) { return false; }
template <typename T>
constexpr bool is_numeric(T const& t) { return is_numeric_impl(&t); }
// Specializations
template <typename T,
typename = std::enable_if<std::is_arithmetic<T>::value>::type>
constexpr bool is_numeric_impl(T const*) { return true; }
template <typename T>
constexpr bool is_numeric_impl(GenericContainer<T> const*) {
return is_numeric((T const*)nullptr);
}
The main benefit being that this solution is open-ended so that other people may reuse the same traits and add specializations; because it uses a white-list.
Boost's enable_if and type traits allow tricks like you need:
template <class T, class Enable = void>
struct is_numeric : public std::is_arithmetic<T> {};
template <class T>
struct is_numeric<T, typename enable_if<is_base_of<GenericContainer<T>, T> >::type>
: public std::is_arithmetic<T> {};
The solution employs SFINAE principle to compile the second version of is_numeric when the template parameter meets the criteria inside enable_if. Notice that the syntax of is_base_of is is_base_of<Base, Derived>. There is more explanation in Boost's enable_if documentation.
Since the relationships in your case are even more complicated, as David Rodriguez kindly mentioned, you should probably make it a bit differently:
template <template <class> class U, class T>
struct is_numeric<U<T>, typename enable_if<is_base_of<GenericContainer<T>, U<T> > >::type>
: public std::is_arithmetic<T> {};
And if you cannot use the libraries themselves, you can always use them as inspiration :)
Did you try :
template <typename T>
struct is_numeric : public std::is_arithmetic<T>{};
template <template<class...> class Container, typename T, typename... Rest>
struct is_numeric<Container<T, Rest...>> : public is_numeric<T>{};
Seems to work for me.
You need to define the is_numeric trait for each container, you cannot just use the base definition.
template <typename T, size_t N>
struct is_numeric< Vector<T,N> > : is_numeric< GenericContainer<T> > // *
{};
Also note that the definition of the is_numeric should be similar the one in the comment, not the one in the question. That is, you want to define is_numeric for a container in terms of whether the nested type is numeric or not (so that you can peel off the different layers).
I need to use a template class which is defined in another template class as parameter of another template as return value in template method. I know it sounds complicated, code below explains it better. Problem is that the code cannot be compiled, it ends with following error:
type/value mismatch at argument 2 in template parameter list for 'template<class T, template<class> class Policy> class Result'
expected a class template, got 'CDummy<T2>::Policy2'
but I'm pretty sure that given class fulfills needs. Problem is that the method, which uses it, is template too and so compiler does not know what exactly CDummy<T2>::Policy2 is. If the Policy2 would not be template, but regular class or if I could fill its argument, I would use typename which would tell the compiler not to worry about it, but how can this be done with template?
// I cannot change this interface - it's given by a library
template <class T, template <class> class Policy>
class Result : public Policy<T>
{
T data;
};
template <class T>
class Policy1
{
};
// I use this for allowing Policy2 to change behaviour according Dummy
// while it keeps template interface for class above
template <class Dummy>
class CDummy
{
public:
template <class T>
class Policy2 : public Policy1<T>
{
};
};
// Both variables are created ok
Result<int, Policy1 > var1;
Result<int, CDummy<float>::Policy2 > var2;
// This is ok, too
template <class T>
Result<T, Policy1 > calc1()
{
return Result<int, Policy1>();
}
// But this ends with the error:
// type/value mismatch at argument 2 in template parameter list for 'template<class T, template<class> class Policy> class Result'
// expected a class template, got 'CDummy<T2>::Policy2'
template <class T1, class T2>
Result<T1, CDummy<T2>::Policy2 > calc2() // <-- Here is the generated error
{
typedef typename DummyTypedef CDummy<T2>;
return Result<T1, DummyTypedef::Policy2>();
}
Notes:
I use gcc 4.7.3 32bit in GNU/Linux Ubuntu 13.04. 32 bit.
For various reasons, I cannot use C++11 standard (yet) and so I cannot use template typedefs
I believe that the name CDummy<T2>::Policy2 is a dependent name in that context and that you should use the template keyword to inform the compiler that it is indeed a template.
template <class T1, class T2>
Result<T1, CDummy<T2>::template Policy2 > calc2() // <-- Here is the generated error
// ^^^^^^^^
additionally the implementation of that same function seems to be wrong also. The order of typedefs is original name, new name, and CDummy<T2> is known to be a type (i.e. there is no need for the typename):
typedef CDummy<T2> DummyTypedef;
The return statement would then be:
return Result<T1, DummyTypedef::template Policy2>();