If one has a
template <class T>
class A{};
// global namespace, static storage duration
static constexpr A<int> a;
is it possible to deduce the type A<int> by passing a as a reference template param such as:
// This question asks to make the syntax in the following line compile:
static_assert(std::is_same<A<int>, typename GetReferenceType<a>::type>::value, "");
// I am aware the next line works, but it's not what I am looking for in this question
static_assert(std::is_same<A<int>, decltype(a)>::value, "");
// This is pseudo code of how this could work in theory
template <const T& RefT, class T> // I know this does not compile, but this shows what I want
struct GetReferenceType{ // which is automatically deduce type `T` without having to
using type = T; // write it out
};
An answer which explains why this is not possible in C++ is as welcome as a solution to make this syntax compile :)
I am mostly asking out of curiosity, since basically everything else can be deduced in templates, but apparently not reference types.
This should also work, but does not fulfill the above syntax requirement:
template <class T>
constexpr auto GetReferenceTypeFunc(const T& t) -> T;
static_assert(std::is_same<A<int>, decltype(GetReferenceTypeFunc(a))>::value, "");
Why I want to do this
I am striving for the most concise syntax.
While Instantiate<decltype(a)> works, it's not ranking highly in the terseness scale, especially if a syntax like Instantiate<a> was possible.
Imagine a does not have a short type A<int> but instead something like
A<OmgWhyIsThisTypeNameSoLong>.
Then, if you want to instantiate a type with A<OmgWhyIsThisTypeNameSoLong>, you'd have to write:
Instantiate<A<OmgWhyIsThisTypeNameSoLong>>;
It just so happens that we already have a global object a, so it would be nice not to have to write that long type but instead Instantiate<a>.
There is of course the option of creating an alias using AOmg = A<OmgWhyIsThisTypeNameSoLong> but I would really like to get around spamming the namespace with another very similar name to A.
In C++20, you might do:
template <auto V>
struct GetReferenceType
{
using type = std::decay_t<decltype(V)>;
};
static_assert(std::is_same<A<int>, GetReferenceType<a>::type>::value);
but decltype seems sufficient.
Demo
// I not only want to deduce A<int> but also int
So you probably want a trait like:
template <typename> struct t_parameter;
template <template <typename > class C, typename T> struct t_parameter<C<T>>
{
using type = T;
};
but simple alternative is to add info directly in A:
template <class T>
class A{
using value_type = T;
};
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 have a problem with inner classes in class templates. I have a template class (say: Matrix<T>), and a subclass (say: Matrix<T>::Row). Now I want to to write a function which operates on instances of the subclass (say: negate(Matrix<T>::Row &)). I tried to declare the function with template<class T> negate(typename Matrix<T>::Row &), but when I try to use it, the compiler tells me that it cannot find a match.
Here's an abstract example:
template<class T>
class A
{
public:
class B
{
};
};
template<class T>
void x(typename A<T>::B &)
{
}
int main()
{
A<int>::B b;
x(b); // doesn't work: Error: Could not find a match
// for x<T>(A<int>::B) needed in main().
x<int>(b); // works fine
}
Why does the compiler does not manage to find x in the first case? Is there a way to modify this that it works (without explicitly specifying the type int)?
(I also have similar problems where x is of the form template<class T, class S> void x(typename A<T>::B &, const S &);, whence I would really like not to be forced to explicitly name all types while doing the call.)
I have tried this with g++ 4.4.3, g++ 4.5.2, and Sun Studio 5.9, all give the same result. Thanks a lot in advance for anything helpful!
How should the compiler be able to deduce this? Imagine the following setup:
struct A { typedef int T; };
struct B { typedef int T; };
template <typename S> void foo(typename S::T);
Now when you say int x; foo(x);, there's no way to match this unambiguously.
The point is that you are not deducing a template parameter from a given class template, but rather just an arbitrary, free-standing type. The fact that that type was defined inside another class is not relevant for that.
That is non-deducible context. That is why the template argument cannot be deduced by the compiler.
Just imagine, you might have specialized A as follows:
template <>
struct A<SomeType>
{
typedef std::map <double, double> B;
};
Now this specialization has a nested type called B which is a typedef of std::map<double,double>.
So how would the compiler deduce the type SomeType, given that A<SomeType>::B is std::map<double, double>?
And in fact, there can be many such specializations, as such:
template <>
struct A<SomeOtherType>
{
typedef std::map <double, double> B;
};
Even this specialization has B as nested type.
Now if I say A<T>::B is std::map<double,double>, then can you say what T is? Is it SomeType? or SomeOtherType?
It would need to deduce the type T for the call to template function x, and template argument deduction is only allowed in a specific set of circumstances:
http://publib.boulder.ibm.com/infocenter/compbgpl/v9v111/index.jsp?topic=/com.ibm.xlcpp9.bg.doc/language_ref/template_argument_deduction.htm
A<T>::B
does not seem to be one of them :/
I can't find the right syntax even after reading cppreference on template params. The following doesn't compile, but hopefully describes what I want to do. What's the correct syntax?
template <class DisplayType>
class DisplayAdapter : public DisplayType
{
};
template<template <typename> DisplayAdapter>
class Painter // Takes an instance of DisplayAdapter, not a type!
{
}
Here is how it's supposed to be used:
struct S{};
int main()
{
DisplayAdapter<S> concreteAdapter;
Painter<concreteAdapter> p;
return 0;
}
Here's Ideone snippet for the whole thing: https://ideone.com/dvbYt8
What you're wanting is not a template template parameter.
A template template parameter is used to pass templates around, like this:
template<template<typename> typename Container>
struct foo {
Container<int> container;
};
int main() {
foo<std::vector> f;
}
As you can see, you can pass template names around with that. Remember that templates are not types, but a blueprint for type, and the language (and the standard) is not treating templates the same way as types.
I assume with your examples your trying to use non-type template parameters?
A non-type template parameter is a template parameter that is a value instead of a type. It can be of any integral types, reference type and pointer type.
For example, look at std::array:
std::array<int, 10> tenInts;
Notice the second parameter is a number. This is because std::array look something like this:
template<typename T, std::size_t N>
struct array { /* ... */ };
The second parameter is an unsigned long int.
You can also pass references and pointers as template parameter:
template<int& i> struct foo {};
template<int* i> struct bar {};
int main() {
// Need at least internal linkage in C++14 and older
// No linkage required since C++17, only static needed.
static int num = 0;
foo<num> f;
bar<&num> b;
}
You can even pass a pointer to any type as reference template parameter:
struct stuff {};
template<stuff& s> struct foo;
int main() {
static stuff s{};
foo<s> f; // works!
}
This seem to be closer to what you wanted. However, you seem to have many many different type you want to send as template parameter, as the type of the instances you want to pass around are templated. For that you'll need C++17 template auto feature:
template<auto& da>
struct Painter {
// ...
};
int main() {
static DisplayAdapter<S> concreteAdapter;
Painter<concreteAdapter> p;
}
And done!
If you don't have C++17 with you don't worry, and simply pass the display type along with your instance (C++14 example):
template<typename DT, DisplayAdapter<DT>& da>
struct Painter {};
// Internal linkage
DisplayAdapter<S> da;
int main() {
Painter<S, da> painter;
}
If it was some simple type like int you would just use type instead of typename keyword e.g
template <int x>
or
template <std::vector<int>::value_type x>
But you can't put object of arbitrary type here.
More about non-type template parameters can be found in another Q&A
You need to add class before DisplayAdater
template <class DisplayType>
class DisplayAdapter : public DisplayType
{
};
template<template <typename> class DisplayAdapter>
class Painter // Takes an instance of DisplayAdapter, not a type!
{
};
https://godbolt.org/g/mV7YRC
Is it possible to get the return type of a template member function at compile time?
I guess I need something along the lines of:
template<class T>
struct SomeClass
{
// T must have a function foo(int), but do not know the
// return type, it could be anything
using RType = ??? T::foo(int) ???; // Is it possible to deduce it here?
}
What you want to do can be achieved by using the decltype operator together with the std::declval template.
decltype(EXPRESSION) yields – at compile time – the type that EXPRESSION would have. The EXPRESSION itself is never evaluated. This is much like sizeof(EXPRESSION) returns the size of whatever EXPRESSION evaluates to without ever actually evaluating it.
There is only one problem: Your foo is a non-static member function so writing decltype(T::foo(1)) is an error. We somehow need to obtain an instance of T. Even if we know nothing about its constructor, we can use std::declval to obtain a reference to an instance of it. This is a purely compile-time thing. std::declval is actually never defined (only declared) so don't attempt to evaluate it at run-time.
Here is how it would look together.
#include <type_traits>
template <typename SomeT>
struct Something
{
using RetT = decltype(std::declval<SomeT>().foo(1));
};
To see that it actually works, consider this example.
struct Bar
{
float
foo(int);
};
struct Baz
{
void
foo(int);
};
int
main()
{
static_assert(std::is_same<float, Something<Bar>::RetT>::value, "");
static_assert(std::is_same<void, Something<Baz>::RetT>::value, "");
}
While this does what I think you have asked for, it is not ideal in the sense that if you attempt to instantiate Something<T> with a T that doesn't have an appropriate foo member, you'll get a hard compiler error. It would be better to move the type computation into the template arguments such that you can benefit from the SFINAE rule.
template <typename SomeT,
typename RetT = decltype(std::declval<SomeT>().foo(1))>
struct Something
{
// Can use RetT here ...
};
If you know the argument types to your function call the following should work:
template<typename T>
struct X
{
typedef typename decltype(std::declval<T>.foo(std::declval<int>())) type;
};
If you don't you can still deduce the type of the function pointer and extract the return type:
template<class F>
struct return_type;
template<class C, class R, class... Args>
struct return_type<R(C::*)(Args...)>
{ using type = R; };
template<typename T>
struct X
{
typedef typename return_type<decltype(&T::foo)>::type type;
};
This will fail if T::foo is an overloaded function or member of T.
Unfortunately it is only possible to know the return type of some expression if you know with what arguments you are going to call it (which, unfortunately, often is a different place from where you need to know the return type)...
Would any one knows according to what rules code below doesn't compile?
template <class T>
struct B
{
typedef T type;
};
template<class T>
struct X
{
};
template<class T>
struct X<B<T>::type*>//HERE I'M PARTIALLY SPECIALIZING (WELL, TRYING TO...)
{
};
Please see comment inside the code.
How do you think that will work? The compiler will look to see if there is a class T somewhere that has a typedef "type" to your class?
It just won't. Even though it's a pointer.
Remember that presumably your B template is presumably specialised in places so that type is not always T*, but it can't deduce it with reverse engineering.
For those who did not understand my answer fully, what you are asking the compiler to do is find a class U such that B::type is the class you pass in as a parameter.
class Foo;
class Bar;
template<> struct B<Foo>
{
typedef int type;
};
template<> struct B<Bar>
{
typedef int type;
};
X<int*> // ambiguous, T is Foo or Bar?
It is difficult to know exactly why you are trying to do what you are. You can do a partial specialization on all pointers and then a total specialization on specific pointers, which could be implement in terms of another template.
You need to use typename keyword as,
template<class T>
struct X<typename B<T>::type*>
{
};
It's because B<T>::type is a dependent name. So typename is required!
--
EDIT:
Even after putting typename, it isn't compiling. I think it's because deduction of type T in B<T> from X<U> is difficult, or possibly impossible, for the compiler. So I believe its non-deduced context.
See a similar example here and the discussion:
Template parameters in non-deduced contexts in partial specializations
However, if you change the specialization to this:
template<class T>
struct X<B<T> >
{
};
Then it becomes the deducible context, and so would compile.
Assuming you already added typename as suggested by Nawaz.
The problem is exactly explained in the error message you encounter: "template parameter is not deducible in partial specialization B<T>::type*. The problem is that B<T>::type and T is exactly the same for all types T. Consider the following example:
class MyClass1 {};
typedef typename B<MyClass>::type MyClass2; //(*)
X<MyClass*> obj1;
X<MyClass2*> obj2;
The result of line (*) is a type MyClass2 which is essentially MyClass1. So, obj1 and obj2 should be objects of the same class. Now, which version of template X should they use?
If you would expect the specialised version of X, tell me if the answer should be the same if line (*) is removed (and obviously obj2 as well). Still obj1 should be the specialised version of X as line (*) has nothing to do with it.
But now we expect the compiler to detect that some type can be potentially declared as B<T>::type although we never do this. We expect the compiler to verify all possible template instantiations to check if there is no strange typedef in one of them.
I hope this clarifies why such specialisation cannot be handled by the compiler.
An alternative that might help
I believe your problem could be attacked by creating a trait class for explicitly marking types that should be handled in a special way. Something like this:
template <bool v>
struct boolean_value {
static const bool value=v;
};
template <typename T>
struct is_my_interesting_type : public boolean_value<false> {};
class MyClass {
...
};
template <>
struct is_my_interesting_type<MyClass> : public boolean_value<true> {};
template <typename T, bool special>
class InternalX {
... //generic version of your template X
};
template <typename T>
class InternalX<T,true> {
... //special version of your template X
};
template <typename T>
class X : public InternalX<T,is_my_interesting_type<T>::value> {};
Also, you might be interesting how it is done in boost library, in particular Boost.Type_Traits
"The argument list cannot be identical to the non-specialized argument list (it must specialize something)"
see partial_specialization at en.cppreference.com