This question already has answers here:
Where and why do I have to put the "template" and "typename" keywords?
(8 answers)
Closed 5 years ago.
I have a template with two template arguments (MyCollection) and another template (TCTools) expecting a template with one template argument as template argument.
I defined a "bridge" (TypedCollection) to get a template with one parameter from MyCollection and an argument for it's first parameter with the aim of passing it to the template template.
This works fine, if I use the bridge with a fixed type as argument, but calling it from yet another template with the argument of that other template will not compile.
#include <iostream>
using std::size_t;
template <class Scalar, size_t size>
struct MyCollection
{
MyCollection()
{
std::cout << "Made collection"
<< std::endl
<< " " << __PRETTY_FUNCTION__
<< std::endl;
}
};
template <class Scalar>
struct TypedCollection
{
template <size_t size>
using value = MyCollection<Scalar, size>;
};
template <template <size_t> class TC>
struct TCTools
{
static TC<10> *make_10_sized()
{
return new TC<10>();
}
};
template <class S>
void test()
{
// Will not compile
TCTools<TypedCollection<S>::value>::make_10_sized();
}
int main()
{
// works
TCTools<TypedCollection<int>::value>::make_10_sized();
test<int>();
return 0;
}
GCC gives the following note:
expected a class template, got ‘TypedCollection<S>::value’
The whole thing has left me very confused. Why is the call in test() not compiling, while the one in main() works just as expected? Is it possible to get test to work?
TCTools<TypedCollection<S>::template value>::make_10_sized()
much like typename (but confusingly different), you have to disambiguate what value is, or the compiler assumes it is a value and not a type or template.
It does this before S is substituted. In theory, a specialization of TypedCollection<S> could make value anything at all, and the compiler doesn't try to guess.
As an aside, if you end up doing more metaprogramming, you'll find having template, non-type and type template arguments to be a real pain.
One approach is to turn all 3 into types.
template<template<class...>class Z>
struct ztemplate_t {
template<class...Ts> using apply=Z<Ts...>;
};
// C++17
template<auto x>
using zvalue_t = std::integral_constant< std::decay_t<decltype(x)>, x >;
// C++11
template<std::size_t x>
using zsize_t = std::integral_constant< std::size_t, x >;
Then we can write templates like apply that take a ztemplate as the first argument and apply it to the rest of the arguments, then zapply which is ztemplate<apply>.
Once that heavy lifting is done, TypedCollection<T> becomes partial_apply_t< zMyCollection_t, T>.
Another is to turn all 3 into constexpr values and do constexpr value-style metaprogramming.
Under this, we end up with type_t< decltype(zpartial_apply( zMyCollection, tag<T> ))>.
But for small one-off libraries, both of these are overkill.
Related
This question already has answers here:
Why does this substitution failure create an error?
(2 answers)
Closed 1 year ago.
I asked a question just before about why std::enable_if<false> cannot be used in SFINAE contexts, as in:
template <typename T, typename DEFAULTVOID = void>
struct TemplatedStruct {};
template <typename T>
struct TemplatedStruct<T, std::enable_if_t<false>> {}; // enable_if expression
// isn't dependent on template type, is always false and so is an error
However in the following example it is dependent on a template argument, but this also creates an error:
#include <type_traits>
template <typename value_t_arg>
struct underlyingtype
{
static inline constexpr bool bIsIntegralType =
std::is_integral_v<value_t_arg>;
template <typename T, typename DEFAULTVOID = void>
struct IsSpecialType {
static inline constexpr bool bIsSpecialType = false;
};
template <typename T>
struct IsSpecialType<T, std::enable_if_t<bIsIntegralType>> {
static inline constexpr bool bIsSpecialType = true;
};
// This also creates an error, this is essentially the same as above
template <typename T>
struct IsSpecialType<T, std::enable_if_t<std::is_integral_v<value_t_arg>>> {
static inline constexpr bool bIsSpecialType = true;
};
};
int main()
{
underlyingtype<int> g1; // Works
underlyingtype<double> g2; // std::enable_if_t<false, void>:
// Failed to specialize alias template
}
In the first case of using std::enable_if_t<false> it fails to compile no matter what I instantiate. However in this other case underlyingtype<int> g1; works while when I instantiate it with a double it then fails to compile, which makes me think they're two different problems.
Edit: I should mention, this fails to compile with Visual Studio Community 2019 16.9.3.
// Failed to specialize alias template
For one, there's no alias template in your code.¹ You're just delcaring bIsIntegralType to be exactly the same thing as std::is_integral_v<value_t_arg>, which is fixed (to false or true) as soon as the instantiation of underlyingtype takes place.
Therefore, the two specializations
template <typename T>
struct IsSpecialType<T, std::enable_if_t<bIsIntegralType>> {
static inline constexpr bool bIsSpecialType = true;
};
// This also creates an error, this is essentially the same as above
template <typename T>
struct IsSpecialType<T, std::enable_if_t<std::is_integral_v<value_t_arg>>> {
static inline constexpr bool bIsSpecialType = true;
};
are the same thing, hence clang says
Class template partial specialization 'IsSpecialType<T>' cannot be redeclared
And this is independent of what value_t_arg you pass to underlyingtype.
When removing either of the two identical specializations, the code is ok as regards underlyingtype<int> g1;, but it is still invalid upon trying to instantiate underlyingtype<double>, because value_t_arg is "blocked" to double in that case, which makes bIsIntegralType be just a false compile-time value, which in turns means that you're passing an always-and-ever-false to std::enable_if_v.
Putting it in another way, when you ask for underlyingtype<double>, the compiler starts instantiating the class underlyingtype with value_t_arg = double; at this point the compiler hasn't even looked at IsSpecialType, but it knows that bIsIntegralType == false, which makes the code for IsSpecialType's specialization invalid as per the previous question.
(¹) An alias template is a templated type alias,
template <typename T>
using new_name = old_name<T>;
whereas in your code there's no using at all, so there couldn't be a type alias, let alone an alias template.
Based on this and the previous question, it looks like you're trying to get into SFINAE and Template Meta-Programming. If I may give you a suggestion, a good way to learn it is to read and understand how the Boost.Hana library works. There's a lot of TMP and SFINAE there, but the quality of the code is high (imho) and the code itself is extremely well documented and, hence, understandable (obviously it takes time).
Consider this line:
std::cout << underlyingtype<double>::IsSpecialType<char>::bIsSpecialType << "\n";
How should we go about interpreting it?
underlyingtype is a template.
underlyingtype<double> is not a template, it is a type, a specific instantiation of underlyingtype.
underlyingtype<double>::IsSpecialType is a template, a member of a (non-template) class type underlyingtype<double> This template has a single parameter T.
underlyingtype<double>::IsSpecialType<char> is an instantiation of the preceding template.
Now, when instantiating a template, its parameters are substituted with actual arguments. Failure to perform such substitution is not an error. In case of underlyingtype<double>::IsSpecialType, the parameter is T. However std::enable_if_t<std::is_integral_v<value_t_arg>>> does not depend on T, so no substitution takes place.
I want to declare a function, which will take as a parameter a variable (let's say, int), which should be parametrized by a class. Speaking in terms of lambda calculus, I want my parameter to have a kind * -> int.
Example of a function I want to be able to write (Spec is the variable):
template <??? Specification, typename T>
auto make_array() {
return std::array<T, Specification<T>>;
}
Since C++14 we have variable templates, so we can do something like this:
template <typename T>
constexpr int digits = std::numeric_limits<T>::digits;
The problem is, how do I pass that into a function? In the notes section of cppreference it is stated that
Variable templates cannot be used as template template arguments.
But does that mean that there is actually no way to pass parametrized variable as a function parameter? What you can do is, for example, create a class which has a static field denoting value, but an obvious drawback is that the users of my function must derive from that class.
I believe there might be some workaround using SFINAE, but I lack skills in that area.
Unless you insist on using a variable template, you can use a type trait:
template <typename T> struct Specification;
you can specialize it for example for int:
template <>
struct Specification<int> {
static constexpr size_t value = 42;
};
and as you want to have different Specifications, pass it as template template parameter:
template <template<class> class S, typename T>
auto make_array() {
return std::array<T, S<T>::value>{};
}
Complete example:
#include <array>
#include <cstddef>
template <template<class> class S, typename T>
auto make_array() {
return std::array<T, S<T>::value>{};
}
template <typename T> struct Specification;
template <>
struct Specification<int> {
static constexpr size_t value = 42;
};
int main(){
auto x = make_array<Specification,int>();
}
Note that I was rather verbose for the sake of clarity. You can save a bit of typing by using std::integral_constant, eg:
template <>
struct Specification<double> : std::integral_constant<size_t,3> {};
As an follow-up on idclev's answer, you can avoid the need to explicitly specialise for the different types for individual "specifications" just by inheriting from e.g. integral_constant. For example, your desired usage was something like
template <template <typename T> int Specification, typename T>
auto make_array() {
return std::array<T, Specification<T>>;
}
template <typename T>
constexpr int digits = std::numeric_limits<T>::digits;
// ...
auto foo = make_array<digits, double>();
However, as you have noted, this is impossible: you cannot pass variable templates as template-template parameters. However, by turning digits into a structure directly you can do this:
template <template <typename T> class Specification, typename T>
auto make_array() {
// we no longer have the guarantee of the type here, unfortunately
// but you can use a static_assert to improve error messages
// (also using `std::size_t` here for correctness)
static_assert(
std::is_convertible<decltype(Specification<T>::value), std::size_t>::value,
"value must be convertible to size_t");
return std::array<T, Specification<T>::value>;
}
// use a type rather than a variable template
// we just inherit from integral constant to save on some typing
// (though you could do this explicitly as well)
template <typename T>
struct digits : std::integral_constant<int, std::numeric_limits<T>::digits> {};
// ...
// same call!
auto foo = make_array<digits, double>();
I know it's generally a bad idea to rely on SFINAE unless absolutely necessary, but I'm curious about how to do the following anyway.
So let's say I have a function that prints a type to the console (class used for partial specialization since it more closely matches my situation):
template <class Ty>
class print
{
public:
print(Ty line)
{
std::cout << line << std::endl;
}
};
Since this is scaled down code, I'm not sure if it would also work with const types, but (because does not in my specific case) let's say the above function does not work with const types. Correct me if I'm wrong, but I believe this would be how you'd accomplish this with partial template specialization?
template <class Ty>
class print <const Ty>
{
public:
print(const Ty line)
{
std::cout << line << std::endl;
}
};
However, is there a way to use the <type_traits> header to do this? I've come across a question that was specific to char* and const char*, but it seems to be different when generalized. Additionally, that question (and answer) is nearly 7 years old.
I've tried the following code (untested) when trying to adapt the answer from the above question to my own situation, but it seems like there should simply be a better way to accomplish this task. In fact, I'm pretty sure my code won't compile (specifically if Ty is already const)
template <class Ty>
struct print_accept_const :
std::enable_if<std::is_same<Ty, Ty>::value || std::is_same<Ty, const Ty>>
{};
template <class Ty, class = print_accept_const<Ty>>
class print
{
print(Ty line)
{
std::cout << line << std::endl;
}
};
Just for reference, I'm using partial template specialization because I'm specializing this print class for std::vector objects, std::set objects, std::unordered_set objects, etc. If there is a way to do this without SFINAE, then I'd be totally open to that.
Edit 1
As asked in a comment, my exact error happens when I try to specialize for std::unordered_set objects.
template <class ValTy>
class print <std::unordered_set<ValTy>>
{
public:
print(std::unordered_set<int> lines) // 'int' instead of 'ValTy' to activate IntelliSense for errors
{
const auto last = --lines.end();
for (auto& line : lines)
{
// IntelliSense, for the line below when 'ValTy' is
// replaced with 'int', says:
//
// no instance of constructor "print<std::unordered_set<ValTy,
// std::hash<ValTy>, std::equal_to<ValTy>, std::allocator<ValTy>>>
// ::print(std::unordered_set<int, std::hash<int>, ...>)" matches
// the argument list. Argument types are (int).
//
print<ValTy> p(line);
}
}
}
Isn't clear what do you exactly want (enable when T is const or isn't const; if you have a function that print value of some types it's necessary wrap it in a template class? Can't you apply SFINAE directly over the function?) anyway... some useful elements...
(1) starting from C++11 there is std::is_const that you can use to check if a type is constant or not
(2) to make an example, a possible way to enable a specialization only for constant types is the following
template <typename T, typename = void>
struct print;
template <typename T>
struct print<T, typename std::enable_if<std::is_const<T>::value>::type>
{
print (T line)
{ std::cout << line << std::endl; }
};
The specialization can be simplified, starting from C++14, using std::enable_if_t
struct print<T, std::enable_if_t<std::is_const<T>::value>>
and, starting from C++17, also using std::is_const_v
struct print<T, std::enable_if_t<std::is_const_v<T>>>
(3) you can enable/disable directly the constructor, but only making is a template one (or SFINAE can't work)
template <typename T>
struct print
{
template <typename U = T,
typename std::enable_if<std::is_const<U>::value, int>::type = 0>
print (U line)
{ std::cout << line << std::endl; }
// possible alternative do-nothing constructor for not const values
template <typename U = T,
typename std::enable_if<! std::is_const<U>::value, int>::type = 0>
print (U)
{ }
};
Observe that, in this case, the SFINAE tests (std::is_same and std::is_const<U>) are (also) over the template parameter of the constructor, U, not over the template parameter of the class, T. Otherwise SFINAE doesn't works.
The first std::enable_if impose that T and U are the same.
This question already has answers here:
C++ templates that accept only certain types
(14 answers)
restrict a template function, to only allow certain types
(3 answers)
Template excluding one type
(3 answers)
Template Class C++ - exclude some types
(3 answers)
Closed 3 years ago.
Following Template class type-specific functions, how can I customize my template code to not compile for certain types?
If the question is not clear, take a look at this example.
///** template class ***/
template<typename T>
class testClass{
testClass();
T parameter;
}
template<typename T>
void testClass<T>::print(){cout<<parameter.value<<endl;}
The above class is supposed to work for the following types:
//** types file **/
class paramtype1{
int value;
}
class paramtype2{
int value;
}
class paramtype3{
}
As you see, paramtype3 doesn't have value, so I get a compile error saying that value is not defined. I know that if I want to specialize a template class function for a certain type (s), I need to do:
template<>
void testClass<paramtype1>::print(){cout<<parameter.value<<endl;}
But, is there any way to do the other way around, only excluding some certain types?
If you want enable/disable the full class/struct, you can use SFINAE and partial specialization.
The following is a C++17 example
template <typename T, typename = void>
struct testClass;
template <typename T>
struct testClass<T, std::void_t<decltype(T::value)>>
{
testClass()
{ };
T parameter;
void print()
{ std::cout << parameter.value << std::endl; }
};
If you only want enable/disable the print() function, you have to templatize it; by example
template <typename U = T>
std::void_t<decltype(U::value)> print()
{ std::cout << parameter.value << std::endl; }
or also
template <typename U = T>
std::void_t<decltype(U::value), std::enable_if_t<std::is_same_v<U, T>>>
print()
{ std::cout << parameter.value << std::endl; }
if you want to be sure that nobody can "hijack" the method explicating the template type calling
testClass<paramtype3>{}.print<paramtype1>():
What I would personally do to exclude permissions to use certain types is:
template <class T, class... Ts>
struct is_any : std::disjunction<std::is_same<T, Ts>...> {};
// https://stackoverflow.com/questions/17032310/how-to-make-a-variadic-is-same
template <typename T>
void do_something() {
static_assert(!is_any<T, int, bool>::value, "do_something<T> cannot be used with T as int or bool");
// code here
}
Allows you to add a custom assertion message aswel, making it easy to realise what's wrong.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is it possible to emulate template<auto X>?
Consider the following working code:
#include <iostream>
template<typename T> struct Traits {};
template<typename T, typename A>
struct Traits<void(T::*)(A)>
{
typedef T Class;
typedef A Arg;
};
template<typename T, typename U, void(T::*MemFun)(U)>
void Builder()
{
std::cout << typeid(T).name() << std::endl;
std::cout << typeid(U).name() << std::endl;
std::cout << typeid(MemFun).name() << std::endl;
}
template<typename T, T arg>
void Builder()
{
return Builder<Traits<T>::Class,Traits<T>::Arg,arg>();
}
class Class
{
public:
void foo(int) { }
};
int main()
{
Builder<decltype(&Class::foo), &Class::foo>();
}
What I'd like to be able to do is something like this to get the same result, without using a macro.
int main()
{
Builder<&Class::foo>();
}
I can't seem to create a template that takes the pointer to member and deduces the type. Any thoughts? The pointer to member must be used as a template argument as opposed to a function parameter because it's used to create a template function (not shown).
Cannot be done. To have a non-type template argument you must provide the type. So either you limit your Builder to only one particular type or else you need an extra argument (first in the list) that is the type of second argument. Now, if you are willing not to use the member pointer as a constexpr... that is simple.
template <typename T, typename M>
struct Builder {
M T::*ptr;
Builder( M T::*ptr ) : ptr(ptr) {}
};
template <typename T, typename M>
Builder<T,M> createBuilder( M T::*ptr ) {
return Builder<T,M>(ptr);
}
int main() {
auto bld = createBuilder( &Class::member );
}
I have spent some time trying to do a similar thing myself.
I don't think it is possible without using the name of the function Class::foo twice. My reasoning is as follows:
to get the type of foo we must either use decltype or give foo as an argument to a template function
in any of the above, the constexpr-ness of foo (allowing it to be used as a template argument) is lost
moreover, we cannot "duplicate" foo inside a template class or function, as it would face the same problem as above
conclusion: we have to type foo twice at the outer scope
It seems one cannot get away without "dirty" macro tricks, even in C++11...
Unfortunately there's no way to do type deduction for the type of non-type template parameters. A macro is really the only option here.