I'm obviously misunderstanding something important about template specialization, because:
template<typename type> const type getInfo(int i) { return 0; }
template<> const char* getInfo<char*>(int i) { return nullptr; }
fails to compile with:
src/main.cpp:19:24: error: no function template matches function
template specialization 'getInfo'
while
template<typename type> type getInfo(int i) { return 0; }
template<> char* getInfo<char*>(int i) { return nullptr; }
works fine. How do I use const with template specializations? What is my rookie mistake?
I am using c++11 on clang++.
Note that, in the first example, the return type is const type, so the const applies to the whole type. If type is char* (as in your specialisation), then the return type is a char * const. This compiles just fine:
template<typename type> const type getInfo(int i) { return 0; }
template<> char* const getInfo<char*>(int i) { return nullptr; }
This makes sense - if specializing the type as a pointer. Why should the template have any say over what the pointer points to?
However, in this situation, I don't see much reason for having the return type be const.
If you need to be able to return string constant just use this:
template<typename type> type getInfo(int i) { return 0; }
template<> const char* getInfo<const char*>(int i) { return nullptr; }
What you tried to do is something like:
const int getInfo( int i ) { return 0; }
it does not make much sense.
Related
I want to write a function that extracts a pointer field from a struct. The requirement is that if I pass the struct as a const argument, the returned type should be const. If not, the returned type should not be const.
For instance,
struct S {
char *p;
};
// approach 1: two overload functions with duplicate body
auto extract(S &input) -> int * {
return reinterpret_cast<int *>(input.p + 12);
}
auto extract(const S &input) -> const int * {
return reinterpret_cast<const int *>(input.p + 12);
}
// approach 2: macro
#define macro_extract(input) (reinterpret_cast<int *>(input.p + 12))
Is there any trick in template or latest C++ standard that can write a strongly typed function without duplicating the body?
EDIT:
Changed the example a bit to reflect more accurately of the real problem.
Here's a solution with a single function template:
template<typename T,
typename = std::enable_if_t<
std::is_same_v<
std::remove_cv_t<
std::remove_reference_t<T>>, S>>>
auto extract(T&& input)
-> std::conditional_t<
std::is_const_v<
std::remove_reference_t<T>>, int const *, int *>
{
return input.p;
}
Here's a demo.
I think it goes without saying that you'd be better off with an overload set. If the function body is large, you can still call the non-const version from the const overload, and add the const there.
if constexpr and auto as return type solution:
#include <type_traits>
struct S {
int *p;
};
template<typename T>
auto extract(T &&input) {
static_assert(std::is_same_v<std::decay_t<decltype(input)>,S>, , "Only struct S is supported");
if constexpr(!std::is_const_v<std::remove_reference_t<decltype(input)>>) {
return input.p;
} else {
return const_cast<const int*>(input.p);
}
}
int main () {
S i;
using t = decltype(extract(i));
static_assert(std::is_same_v<t,int*>);
S const i_c{0};
using t_c = decltype(extract(i_c));
static_assert(std::is_same_v<t_c,const int*>);
return 0;
}
PLZ look at the ISO proposal:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4388.html
And the std::experimental::propagate_const spec:
https://en.cppreference.com/w/cpp/experimental/propagate_const
Or one can implement his own version of propagate_const.
have fun,
FM
SFINAE should be able to do this. The approximate format is:
template<class T,
class allow=std::enable_if_t<std::is_base_of_v<S, T>>>
auto extract(T&& input) -> decltype(input.p) {
return input.p;
}
Basically using universal forwarding references to make it work for anything: S, S&, const S&, S&&, volatile S&, etc.
So, I have a template class, which must call a callback function at some time. That callback function takes const T template as an argument.
I pass that function's pointer to a template class Boom<void*>. However, the const T argument of that callback is interpreted as just T.
But this is the case only with void*.
The code:
//main.cpp
void kolbek(const void* val)
{
if(val)
printf("Value is: %d\n", *((int*)val));
else
printf("Value ptr = NULL\n");
}
int main()
{
Boom<void*> bomba;
bomba.setCallback(kolbek); //error!
int* nuint = new int(5);
bomba.callCallback((void*)nuint);
delete nuint;
return 0;
}
//boom.h
template<typename T>
class Boom
{
private:
void (*someCallback)(const T) = nullptr;
public:
Boom(){ }
~Boom(){ }
void setCallback(void (*callbk)(const T));
void callCallback(const T val);
};
//boom.cpp
template<typename T>
void Boom<T>::setCallback(void (*callbk)(const T))
{
this->someCallback = callbk;
}
template<typename T>
void Boom<T>::callCallback(const T val)
{
if(someCallback)
(*someCallback)(val);
else
printf("Bad! Callback's NULL!\n");
}
template class Boom<int>;
template class Boom<void*>;
And when trying to compile this, an error is thrown:
error: invalid conversion from 'void (*)(const void*)' to 'void (*)(void*)' [-fpermissive]
error: initializing argument 1 of 'void Boom<T>::setCallback(void (*)(T)) [with T = void*]' [-fpermissive]
How to fix it? Seems like only void* pointers are wrongly interpreted.
Your problem comes from confusing two different consts. This is why it can be helpful to write T const instead of const T - it makes textual substitution not lie to you.
Boom's callback takes a T const which in your instatiation is void* const (not const void*!!): it's a const pointer to non-const void. kolbek's argument takes a void const* - a pointer to const void. Those aren't the same type. You can do a qualification conversion from the former to the latter, but not the other way around (you'd be casting away const!). This is your compiler error.
The simplest solution is not not have Boom add const. It's unnecessary. Use the T as provided and use Boom<const void*>.
In the case mentioned in comment you could create helper struct to make your template dereferenced parameter const:
template<class T>
struct ptr_constantizer {
using type = const T;
};
template<class T>
struct ptr_constantizer<T*> {
using type = const T*;
};
template<typename T>
class Boom
{
private:
void (*someCallback)(typename ptr_constantizer<T>::type) = nullptr;
public:
Boom(){ }
~Boom(){ }
void setCallback(void (*callbk)(typename ptr_constantizer<T>::type)) { }
void callCallback(const T val) { }
};
void foo(const void *ptr) {
}
void fooi(const int non_ptr) {
}
int main() {
Boom<void *> b;
Boom<int> bi;
b.setCallback(&foo);
bi.setCallback(&fooi);
}
The code assumes you are using c++11 as you used nullptr in your example...
Please, consider the code below:
template<typename T>
bool function1(T some_var) { return true; }
template <typename T>
bool (*function2())(T) {
return function1<T>;
}
void function3( bool(*input_function)(char) ) {}
If I call
function3(function2<char>());
it is ok. But if I call
function3(function2());
compiler gives the error that it is not able to deduction the argument for template.
Could you, please, advise (give an idea) how to rewrite function1 and/or function2 (may be, fundamentally to rewrite using classes) to make it ok?
* Added *
I am trying to do something simple like lambda expressions in Boost.LambdaLib (may be, I am on a wrong way):
sort(some_vector.begin(), some_vector.end(), _1 < _2)
I did this:
template<typename T>
bool my_func_greater (const T& a, const T& b) {
return a > b;
}
template<typename T>
bool my_func_lesser (const T& a, const T& b) {
return b > a;
}
class my_comparing {
public:
int value;
my_comparing(int value) : value(value) {}
template <typename T>
bool (*operator<(const my_comparing& another) const)(const T&, const T&) {
if (this->value == 1 && another.value == 2) {
return my_func_greater<T>;
} else {
return my_func_greater<T>;
}
}
};
const my_comparing& m_1 = my_comparing(1);
const my_comparing& m_2 = my_comparing(2);
It works:
sort(a, a + 5, m_1.operator< <int>(m_2));
But I want that it doesn't require template argument as in LambdaLib.
Deduction from return type is not possible. So function2 can't be deduced from what return type you expect.
It is however possible to deduce cast operator. So you can replace function2 with a helper structure like: Unfortunately there is no standard syntax for declaring cast operator to function pointer without typedef and type deduction won't work through typedef. Following definition works in some compilers (works in G++ 4.5, does not work in VC++ 9):
struct function2 {
template <typename T>
(*operator bool())(T) {
return function1<T>;
}
};
(see also C++ Conversion operator for converting to function pointer).
The call should than still look the same.
Note: C++11 introduces alternative typedef syntax which can be templated. It would be like:
struct function2 {
template <typename T>
using ftype = bool(*)(T);
template <typename T>
operator ftype<T>() {
return function1<T>;
}
};
but I have neither G++ 4.7 nor VC++ 10 at hand, so I can't test whether it actually works.
Ad Added:
The trick in Boost.Lambda is that it does not return functions, but functors. And functors can be class templates. So you'd have:
template<typename T>
bool function1(T some_var) { return true; }
class function2 {
template <typename T>
bool operator()(T t) {
function1<T>;
}
};
template <typename F>
void function3( F input_function ) { ... input_function(something) ... }
Now you can write:
function3(function2);
and it's going to resolve the template inside function3. All STL takes functors as templates, so that's going to work with all STL.
However if don't want to have function3 as a template, there is still a way. Unlike function pointer, the std::function (C++11 only, use boost::function for older compilers) template can be constructed from any functor (which includes plain function pointers). So given the above, you can write:
void function3(std::function<bool ()(char)> input_function) { ... input_function(something) ... }
and now you can still call:
function3(function2());
The point is that std::function has a template constructor that internally generates a template wrapper and stores a pointer to it's method, which is than callable without further templates.
Compiler don't use context of expression to deduce its template parameters. For compiler, function3(function2()); looks as
auto tmp = function2();
function3(tmp);
And it don't know what function2 template parameter is.
After your edit, I think what you want to do can be done simpler. See the following type:
struct Cmp {
bool const reverse;
Cmp(bool reverse) : reverse(reverse) {}
template <typename T> bool operator()(T a, T b) {
return reverse != (a < b);
}
};
Now, in your operator< you return an untyped Cmp instance depending on the order of your arguments, i.e. m_2 < m_1 would return Cmp(true) and m_1 < m_2 would return Cmp(false).
Since there is a templated operator() in place, the compiler will deduce the right function inside sort, not at your call to sort.
I am not sure if this help you and I am not an expert on this. I have been watching this post since yesterday and I want to participate in this.
The template cannot deduce it's type because the compiler does not know what type you are expecting to return. Following is a simple example which is similar to your function2().
template<typename T>
T foo() {
T t;
return t;
};
call this function
foo(); // no type specified. T cannot be deduced.
Is it possible to move the template declaration to the class level as follows:
template<typename T>
bool my_func_greater (const T& a, const T& b) {
return a > b;
}
template<typename T>
bool my_func_lesser (const T& a, const T& b) {
return b > a;
}
template <typename T>
class my_comparing {
public:
int value;
my_comparing(int value) : value(value) {}
bool (*operator<(const my_comparing& another) const)(const T&, const T&) {
if (this->value == 1 && another.value == 2) {
return my_func_greater<T>;
} else {
return my_func_greater<T>;
}
}
};
and declare m_1 and m_2 as below:
const my_comparing<int>& m_1 = my_comparing<int>(1);
const my_comparing<int>& m_2 = my_comparing<int>(2);
Now you can compare as follows:
if( m_1 < m_2 )
cout << "m_1 is less than m_2" << endl;
else
cout << "m_1 is greater than m_2" << endl;
I know this is simple and everyone knows this. As nobody posted this, I want to give a try.
The following code compiles fine:
template<typename T>
void f(const T &item) { return; }
int main()
{
f("const string literal");
}
Compilation succeeded at ideone : http://ideone.com/dR6iZ
But when I mention the return type, it doesn't compile:
template<typename T>
T f(const T &item) { return item; }
int main()
{
f("const string literal");
}
Now it gives error:
prog.cpp:6: error: no matching function for call to ‘f(const char [21])’
Code at ideone : http://ideone.com/b9aSb
Even if I make the return type const T, it doesn't compile.
My question is :
Why does it not compile?
What does the return type has to do with the error and the function template instantiation?
You cannot return an array from a function, so template instantiation fails, and there's no matching function.
You get this particular error because of SFINAE - It's not really an error that the compiler cannot instantiate your function, it is an error that there's no matching function.
You can return a reference to an array - returning T const & will work.
EDIT: In response to comments:
First, this is actually a decent example of SFINAE.
template<typename T> T f(const T &item) { return item; }
char const * f(void const * item) { return 0; }
int main() {
f("abc");
}
When the compiler compiles this, it'll first try to instantiate the templated f, to create an exact match for the type const char [3]. This fails, because of the mentioned reasons. It'll then select the inexact match, the plain function, and in the call decay the const char [3] to a const char *.
it looks as though you tell it that you plan on returning a T, but then you actually return a const T&. maybe try changing the declaration to:
template<typename T>
const T& f(const T& item) { return item; }
or maybe change the return value to a dereferenced version of the argument:
template<typename T>
T f(const T& item) { return (*item); }
I have a template function:
template<typename T>
void foo(const T& value) { bar(value); x = -1; }
I want to specialize it for a set of types:
template<>
void foo<char>(const char& value) { bar(value); x = 0; }
template<>
void foo<unsigned char>(const unsigned char& value) { bar(value); x = 1; }
It works ok. When I compile this:
template<>
void foo<char*>(const char*& value) { bar(value); x = 2; }
I get an error:
error C2912: explicit specialization; 'void foo(const char *&)' is not a specialization of a function template
Is it possible to specialize with char* pointer type parameter without typedef'ing it?
Sure. Try this:
template<>
void foo<char*>(char* const& value) {...}
This is because const char* means pointer to const char, not a const pointer to char.
You should generally just avoid function template specializations: you'll have far less problems if you provide non-template overloads instead. Read more: Why Not Specialize Function Templates?