In the following code yields (LiveExample):
Class template pointer
Class template pointer
Template pointer
Template array with size 10
#include <iostream>
#include <utility>
#include <type_traits>
template <class TVar>
class CVar
{
public:
void classTemplate(TVar *) const
{
std::cout << "Class template pointer\n";
}
template<unsigned sz>
void classTemplate(TVar(&)[sz]) const
{
std::cout << "Class template array with size " << sz << "\n";
}
};
template<typename T>
void regTemplate(T)
{
std::cout << "Template pointer\n";
}
template<typename T, unsigned sz> void regTemplate(T(&)[sz])
{
std::cout << "Template array with size " << sz << "\n";
}
int main()
{
unsigned int test[10] = {};
CVar<unsigned> *cFoo = new CVar<unsigned>();
cFoo->classTemplate(&test[0]);
cFoo->classTemplate(test);
regTemplate(&test[0]);
regTemplate(test);
}
Why when I overload the template in the class, it cannot resolve the desired functionality - that is, cFoo->classTemplate(test); is called, then the response will be Class template array with size 10?
How do I achieve my desired result in 1 without changing cFoo->classTemplate(test);? Note that void CVar<TVar>::classTemplate(TVar(&)[sz]) const may change if needed
Other things equal, overload resolution prefers non-templates over templates. In classTemplate case, one overload is a function template while the other is a non-template member function.
In regTemplate case, both are function templates: the one taking an array is chosen because it's more specialized. Indeed, if you change the first overload to take T*, the call becomes ambiguous.
Related
I have a case where I need to have a forwarding function defined before a template base function is defined/declared. However, if I call the forwarding function (fwd) that in turn calls the base function test, it says that the base template function is not visible (see the code below). However, if test is called directly, everything works.
So my question is this, is it possible to have the forwarding function make a call to a base template function that is defined later in the compilation unit (before it is used but after the forwarding function)? If not, do I have any options to work around this? I would like to avoid a forward declaration before fwd as that would make use of the library I am developing harder. I think if I could force fwd to be inline it would solve the problem but I have no way of doing that unless a macro is used.
#include <iostream>
#include <vector>
template<typename T, std::enable_if_t<std::is_scalar<T>::value, int> = 0>
void test(const T& t)
{
std::cout << "Scalar" << std::endl;
}
template<typename T>
void fwd(T&& t)
{
test(std::forward<T>(t));
}
template<typename T>
void test(const std::vector<std::vector<T>>& t)
{
std::cout << "vector vector of T" << std::endl;
}
int main(int argc, const char * argv[]) {
test(1); //OK, prints Scalar
fwd(1); //OK, prints Scalar
test(std::vector<std::vector<int>>()); //OK, prints vector vector of T
// Causes compile error: Call to function 'test' that is neither visible in the template definition
// nor found by argument dependent lookup
fwd(std::vector<std::vector<int>>());
return 0;
}
The name test in fwd is a dependent name. It will be resolved into two steps:
Non-ADL lookup examines function declarations ... that are visible from the template definition context.
ADL examines function declarations ... that are visible from either the template definition context or the template instantiation context.
Given that the relative order of test and fwd should not be changed, one possible solution is to use a fake tag struct in the namespace to activate ADL:
namespace my_namespace
{
struct Tag {};
template<typename T, std::enable_if_t<std::is_scalar<T>::value, int> = 0>
void test(const T& t, Tag = Tag{}) {
std::cout << "Scalar" << std::endl;
}
template<typename T>
void fwd(T&& t) {
test(std::forward<T>(t), Tag{});
}
template<typename T>
void test(const std::vector<std::vector<T>>& t, Tag = Tag{}) {
std::cout << "vector vector of T" << std::endl;
}
}
int main() {
my_namespace::test(std::vector<std::vector<int>>()); // OK
my_namespace::fwd(std::vector<std::vector<int>>()); // OK, too
}
Demo
Depending on what test overloads you have, another solution might be to wrap these functions into structs and use template specialization instead of function overloading:
template<class T>
struct Test {
static void op(const T& t) {
std::cout << "Scalar" << std::endl;
}
};
template<typename T>
void fwd(T&& t) {
Test<std::decay_t<T>>::op(std::forward<T>(t));
}
template<class T>
struct Test<std::vector<std::vector<T>>> {
static void op(const std::vector<std::vector<T>>& t) {
std::cout << "vector vector of T" << std::endl;
}
};
int main() {
fwd(1);
fwd(std::vector<std::vector<int>>());
}
Demo
I am constructing a library that makes use of expression templates, where I make heavily use of templated functions in classes. All my code is running and recently I decided to make the main class templated to allow for using it on data of different kinds. However, I can no longer specialize my functions. How do I solve this? I have attached a small test program that shows the problem.
My previous Animal class was not templated and then this code works fine.
#include<iostream>
#include<vector>
// Example templated class with templated function
template<class T>
class Animals
{
public:
template<class X>
void printFood(const X& x) const { std::cout << "We eat " << x << "!" << std::endl; }
private:
std::vector<T> animals;
};
// How do I specialize?
template<class T> template<>
void Animals<T>::printFood(const unsigned int& x) const { std::cout << "We don't want to eat " << x << "!" << std::endl; }
// Main loop;
int main()
{
Animals<double> doubleAnimals;
const int banana = 42;
doubleAnimals.printFood(banana);
const unsigned int apple = 666;
doubleAnimals.printFood(apple);
return 0;
}
This simply isn't possible
[temp.expl.spec]
16 In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well.
You should specialize your class first. Then specialize function:
template<> template<>
void Animals<double>::printFood(const unsigned int& x) const { std::cout << "We don't want to eat " << x << "!" << std::endl; }
You can't partially specialize template members of non-specialized template class. This is consistent with prohibiting partial specialization of template functions (think of template class as the member function's first parameter). Use overloading instead:
template<class T>
class Animals
{
public:
template<class X>
void printFood(const X& x) const { std::cout << "We eat " << x << "!" << std::endl; }
void printFood(const unsigned int& x) const { std::cout << "We don't want to eat " << x << "!" << std::endl; }
private:
std::vector<T> animals;
};
I would like to extract the type of the member a member pointer points to.
template<someType myClass::*member>
void demo(myClass& instance, void* ptr) {
instance.*member = *reinterpret_cast<someType*>(ptr); // can the someType in this line be deduced from member?
}
I tried using decltype as suggested in the comments, however I have issues with this:
instance.*member= static_cast<decltype(instance.*member)>((*buffer)[offset]);
buffer is a std::shared_ptr<std::vector<uint8_t>>,
someType is uint32_t
I get the following error message:
error: invalid static_cast from type
‘__gnu_cxx::__alloc_traits >::value_type
{aka unsigned char}’ to type ‘uint32_t& {aka unsigned int&}’
As far as I understand decltype(instance.*member) with member defined as uint32_t instance::*member yields a reference uint32_t& rather than uint32_t. I tried to pass instance by value and the error remains. I am aware of std::remove_reference however, I do not understand how the reference gets to be there in the first place.
A further improvement would be if I could extract the someType without a class instance. However I have no clue how to achieve this, while I can get the class without a pointer by having the std lib like:
template <T*>
struct removePointer {
typedef T type;
}
I have no Idea how to write this in a form where I can get the someType part of the class, without knowing the class first. I could write something like the following however I would still have to pass the class naem and typename explicitly, is there a way to extract these automatically? Furthermore the following doe not compile in the first place (http://ideone.com/8VlKO4):
#include
using namespace std;
template <class C,typename T, T C::*v>
struct getPointerType {
typedef T type;
};
class Test {
int value;
};
int main() {
int Test::*member=nullptr;
cout << typeid(getPointerType<Test, int, decltype(member)>::type) << std::endl;
return 0;
}
Frankly, it's a bit hard to understand what you're trying to achieve, so I will focus on the updated part.
Clearly, you can not pass types (derived from decltype) as value arguments to the template. Moreover, you can not pass non constexpr values as template arguments (so you can not just stick the member variable into the template argument and expect it to compile).
However, you can rely on compiler to be able to deduce a correct function to call on non costexpr variable:
template <class C, typename T>
T getPointerType(T C::*v);
class Test {
int value;
};
int main() {
int Test::*member=nullptr;
cout << typeid(decltype(member)).name() << std::endl;
cout << typeid(decltype(getPointerType(member))).name() << std::endl;
return 0;
}
The above will print:
M4Testi //int Test::*
i //int
It is, of course, possible to "abuse" the template substitution even more:
template <typename M>
struct getPointerType {
template <typename C, typename T>
static T get_type(T C::*v);
typedef decltype(get_type(static_cast<M>(nullptr))) type;
};
class Test {
int value;
};
int main() {
int Test::*member=nullptr;
cout << typeid(getPointerType<decltype(member)>::type).name() << std::endl;
return 0;
}
The output will be the expected "i".
Here's a solution using template specialization technique :
#include <type_traits>
template <class T>
struct member_type_helper;
template <class C, class T>
struct member_type_helper<T C::*> { using type = T; };
template <class T>
struct member_type
: member_type_helper<typename std::remove_cvref<T>::type> {};
// Helper type
template <class T>
using member_type_t = member_type<T>::type;
Example of usage :
#include <iostream>
struct Foo { int i; };
int main()
{
auto ptr1 = &Foo::i;
auto const& ptr2 = &Foo::i;
volatile auto const ptr3 = &Foo::i; // let's go crazy
// prints true true true
std::cout << std::boolalpha
<< std::same_as<int, member_type_t<decltype(ptr1)>> << '\n'
<< std::same_as<int, member_type_t<decltype(ptr2)>> << '\n'
<< std::same_as<int, member_type_t<decltype(ptr3)>> << '\n';
}
I guess std::remove_cvref might be an overkill for most usage, but hey, it's free. If your compiler isn't >C++20 compliant, you could use std::remove_cv instead (> C++11).
I've got a template function, bar, that takes a reference to an array as a parameter. I'd like to take the argument and pass it to another function, but only after reducing the size of the array by one, and skipping past the first element of the array.
#include <iostream>
template <typename T>
void foo(const T& t)
{
std::cout << sizeof(t) << std::endl;
std::cout << t[0] << std::endl;
}
template <typename T>
void bar(const T& t)
{
std::cout << sizeof(t) << std::endl;
std::cout << t[0] << std::endl;
foo(t); // Magic is desired here!
}
int main()
{
char array[] = "ABCD";
bar(array);
}
The above prints out:
5
A
5
A
I'd like it to print out:
5
A
4
B
You can do this with two template parameters, one for the array type and one for the size of the array.
template <typename T, int N>
void bar(const T (&t)[N])
{
// ...
foo(reinterpret_cast<const T(&)[N-1]>(t[1]));
}
Copying the array may be necessary to get a reference. I hope this answer will draw attention to the real subject of your question..
Calling foo with an appropriate-looking (and generic) cast looks as follows
reinterpret_cast<typename std::remove_reference<decltype(t[0])>::type [sizeof(t)-1]>(t+1)
but the above is invalid - you cannot cast const char* to const char[4]; Also you cannot obtain a reference in another way since an array cannot be copy constructed. So you may need to either copy or use std::array in c++11, which really boils down to having two template parameters.
Here is a valid solution however:
typedef typename std::remove_reference<decltype(t[0])>::type element_type;
foo(reinterpret_cast<element_type(&) [sizeof(T)-1]>(t[1]));
I play around with template specialization and SFINAE.
As for the following example, the things seems easy:
template <class T>
void Do(T t, typename std::enable_if<std::is_integral<T>::value >::type* = 0)
{
cout << "is integer" << endl;
}
template <class T>
void Do(T t, typename std::enable_if<std::is_floating_point<T>::value >::type* = 0)
{
cout << "is float" << endl;
}
No I tried std::is_array, but the specialization with std::is_array is never used.
So I tried out why is_array never matches:
template <int num>
void Do( int a[num])
{
cout << "int array size " << num << endl;
}
void Do( int* x)
{
cout << "int*" << endl;
}
...
int e[] = { 1,2,3 };
Do(e);
...
The first mystery for me is, that the specialization with "int a[num]" did never catch! The function parameter always has the type int*.
If I use reference types I got the "correct" result:
template <int num>
void Do( int (&a)[num])
{
cout << "int array size " << num << endl;
}
void Do( int* &x)
{
cout << "int*" << endl;
}
So my question comes up: Is there a reasonable usage of std::is_array in combination with template function parameters? I know that
cout << boolalpha << std::is_array<decltype(e)>::value << endl;
will give me the correct result. But declaring the template selection manually gives me no functional add on. I there any way to detect (with or without SFINAE) that an template specialization from function parameters fits to an array?
I think you got it yourself - pass arrays to template functions by reference, if you want to use their type in the secialization.
The reason you want to do this is array-to-pointer decay, which is one of the few implicit conversions that happen to template function arguments before they are matched to the parameter types. That's why T was a pointer when you tried to check that it is an array type in DoIt. However, array-to-pointer decay does not happen when the target type is reference type. So, to sum up:
template <class T>
void Do(T& t, typename std::enable_if<std::is_array<T>::value >::type* = 0)
should work.
BTW the boring way of not using SFINAE
template <class T, unsigned N>
void Do(T (&t)[N])
works too.