I'm trying to use a function pointer with template as an argument. But the compiler seems to have trouble handling lambda and nullptr.
Everything is fine when I change void (*callback)(T input) to void (*callback)(int input) in the following code.
Is this compiler behavior specified by the C++ standard?
The compile command I use is $ g++ main.cpp -std=c+11 but the same behavior found in Visual Studio 2019.
template <class T>
int dummy (T tmp, void (*callback)(T input)) {
// Doesn't do anything, just trying to compile
// If I change (T input) to (int input), it compiles fine
if (callback)
return 1;
else
return 0;
}
void callback (int input) {
return;
}
int main () {
int tmp = 10;
auto callback_lambda = [](int input) -> void {
return;
};
dummy(tmp, callback); // Compiles OK
dummy(tmp, callback_lambda); // Error: mismatched types 'void (*)(T)' and 'main()::<lambda(<int>)'
dummy(tmp, nullptr); // Error: no matching function for call to 'dummy(int&, std:nullptr_t)'
return 0;
}
The problem is that implicit conversion won't be considered in template argument deduction.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
So when passing lambda and nullptr, the conversions to the function pointer aren't considered, the template parameter T can't be deduced for the 2nd function argument and then cause the error.
You can make the 2nd function parameter to non-deduced context, to exclude it from deduction, with the help of std::type_identity.
type_identity can be used to block template argument deduction:
e.g.
template <class T>
int dummy (T tmp, void (*callback)(std::type_identity_t<T>)) {
...
}
LIVE
PS: If your compiler doesn't support std::type_identity (since C++20), you can define your own version, it's not difficult.
Related
I hope you are doing great.
I have a class for which I wants its constructor to take an undefined number of arguments, then I though about variadic template.
I can instantiate objects using constructor with no parameters and also with just one parameters, but when I try to use a constructor with two parameters, I get a compile time error like:
error: no matching function for call to 'pinBase::init(GPIO_TypeDef* const&, const long unsigned int&)'
pinBase(const Args& ...rest){init(<----MARKS ERROR HERE--->rest...);}
This is how I conceived my class:
//pinBase.hh
class pinBase
{
private:
GPIO_TypeDef *_instance;
GPIO_PIN _pin = PIN[0];
GPIO_MODE _mode = INPUT;
//these are the functions to which each individual expansion should be called
void _init();
void _init(GPIO_TypeDef* GPIOx);
void _init(GPIO_PIN pin);
//one of these two functions gets called by the constructor either with void or param of certain type
void init(){_init();}
template <class T>
void init(T t){_init(t);}
public:
//constructor accepts void or multiple params ( ... can be empty)
template <class ...Args>
pinBase(const Args& ...rest){init(rest...);}
~pinBase();
}
This is my implementation file:
//pinBase.cpp
#include "pinBase.hh"
pinBase::~pinBase()
{
}
void pinBase::_init()
{
uint8_t temp = 124; // I have set breakpoint here
}
void pinBase::_init(GPIO_TypeDef* GPIOx)
{
_resetValues = *GPIOx; // I have set breakpoint here
}
void pinBase::_init(GPIO_PIN pin)
{
_pin = pin; // I have set breakpoint here
}
This is main file:
//main.cpp
#include "stdint.h"
#include "pinBase.hh"
pinBase pin; //works perfect
pinBase otropin(GPIOA);//works perfect
pinBase andererpin(PIN_0);//works perfect
/**When commented,the program runs well
*and gets to each of the breakpoints
*with no problems at all.
**when uncommented, causes compile time error.
*/
pinBase anotherpin(GPIOA, PIN_0);
int main(void)
{
while (1)
{
/* code */
}
return 0;
}
You clarified that you expect a single call to init() for each variadic parameter.
What you are looking for is a fold expression (requires a C++17 compiler, or later):
template <class ...Args>
pinBase(const Args& ...rest){ (init(rest), ...);}
The syntax you're using results in a single call to init(), with all parameters forwarded in the single function call. The parameter pack gets expanded into a single, forwarded, parameter pack.
A fold expression, on the other hand, should produce your expected result (and you should not need an init() overload with no parameters).
The problem is not with the constructor itself but with the method pinBase::init. In particular, pinBase::init has only 1 parameter so you cannot call it with 2 or more arguments. That is, when you wrote:
init(rest...)
In the above, you're calling pinBase::init by passing all the arguments that the parameter pack has. But since init currently only has one parameter of type T, it cannot be called with 2 or more arguments.
To solve this you can make init a variadic as well as shown below:
class pinBase
{
private:
//------------vvvvvvvv------------->T is a template parameter pack
template <class... T>
//-------------vvv----------------->t is a function parameter pack
void init(T... t){}
//other code here as before
};
Working demo
I'm playing around with some C++11 features, and I encountered the following:
#include <iostream>
#include <vector>
template <class T>
void map(std::vector<T>& values, T(*func)(T)) {
for (int &value : values) {
value = func(value);
}
}
int mul2(int x) {
return 2*x;
}
auto mul3 = [](int value) {
return value * 3;
};
int main() {
std::vector<int> v = { 1,2,3,4,5 };
map(v, mul3);
for (auto value : v) {
std::cout << value << std::endl;
}
}
using map with mul2 works as expected, but when I use the mul3 function it gives a compilation error. I expected that auto in this case would give me a int function pointer, but it seems that is not the case here. Anybody could explain this behaviour?
The lambda can implicitly be converted to a function pointer, but that's not what's failing here. Rather, the compiler is failing to deduce T because the lambda-to-function-pointer conversion doesn't happen during deduction.
main.cpp:5:6: note: template argument deduction/substitution failed:
main.cpp:21:16: note: mismatched types 'T (*)(T)' and '<lambda(int)>'
21 | map(v, mul3);
| ^
The compiler can make the connection between T(*)(T) and int(*)(int) and it can make the connection between int(*)(int) and the lambda type, but it can't make the connection between T(*)(T) and the lambda type.
You can fix this by making one of the two connections for it: explicitly specifying the function's template argument, or casting the lambda to the function pointer type. The first skips the deduction step an then the implicit lambda-to-function-pointer conversion succeeds. The second allows deduction to succeed because the second parameter is a compatible function pointer type.
// Option 1: Specifying T allows implicit conversion of the lambda to fnptr
map<int>(v, mul3);
// Option 2a: Cast of lambda to fnptr allows T to be deduced
map(v, static_cast<int(*)(int)>(mul3));
// Option 2b: Converting lambda to fnptr at mul3 initialization allows T to be deduced
int (*mul3)(int) = [](int value) {
return value * 3;
};
However, I would recommend fixing the issue a different way -- there's no reason the function has to work with vectors, function pointers, and ints. Why can't it work with linked lists, functors, and doubles? There's really no reason to constrain the types like this; just let them be whatever they are and see if the instantiation succeeds:
template <class TContainer, TFunction>
void map(TContainer & values, TFunction const & func) {
for (auto &value : values) {
value = func(value);
}
}
Expanded from my comment (when the question was closed), you can template away the function details using the functor template "pattern":
template <class T, typename Functor>
void map(std::vector<T>& values, Functor func) {
for (int &value : values) {
value = func(value);
}
}
See here for full example: https://godbolt.org/z/fdHvAP
Hi I was trying to implement a C++ concept-like feature (C++14) in C++11. The idea is just to write the wrapper function for std::for_each() algorithm where I just check whether the 3rd argument is a function or not. So I wrote the following code, however I am not able to compile it as it should be. I am using Ubuntu12.04 with gcc4.8.1.
test_1.cpp
#include<type_traits>
#include<iostream>
#include<vector>
#include<algorithm>
void display(int& val) {
std::cout <<val<<std::endl;
}
template<typename T>
constexpr bool Is_valid_function(T& a) {
return std::is_function<T>::value;
}
template<typename T>
void check_valid_function(T& a) {
static_assert(Is_valid_function(a), "Not The Valid Function");
}
template <class InputIterator, class Function>
Function my_for_each(InputIterator first, InputIterator last, Function f) {
/* Concept Like Implementation to just check whether f is function or not */
check_valid_function(f);
return for_each(first, last, f) ;
}
void learn_static_assert_and_typetraits(void)
{
std::vector<int> vec_x{1,2,3,4,5};
my_for_each(vec_x.begin(), vec_x.end(), display);
}
int main(int argc, const char* argv[]) {
learn_static_assert_and_typetraits();
return 0;
}
I am getting the following compilation error from which I can see that the static_assert() fails which is not correct as display is valid function.
Compilation Error
test_3.cpp: In instantiation of ‘void check_valid_function(T&) [with T = void (*)(int&)]’:
test_3.cpp:27:26: required from ‘Function my_for_each(InputIterator, InputIterator, Function) [with InputIterator = __gnu_cxx::__normal_iterator >; Function = void (*)(int&)]’
test_3.cpp:35:50: required from here
test_3.cpp:19:3: error: static assertion failed: Not The Valid Function
static_assert(Is_valid_function(a), "Not The Valid Function");
^
However if I do the same thing for the other type_traits function, I am getting the following error which is correct and expected.
test_2.cpp
#include<type_traits>
template<typename T>
constexpr bool Is_floating_point(T& a) {
return std::is_floating_point<T>::value;
}
template<typename T>
void f(T& a) {
static_assert(Is_floating_point(a), "Non-Float Type Data");
}
void learn_static_assert_and_typetraits(void) {
float y{10.0};
f(y);
int x{100};
f(x);
}
int main(int argc, const char* argv[]) {
learn_static_assert_and_typetraits();
return 0;
}
Compiler Output
test_2.cpp: In instantiation of ‘void f(T&) [with T = int]’:
test_2.cpp:19:6: required from here
test_2.cpp:11:3: error: static assertion failed: Non-Float Type Data
static_assert(Is_floating_point(a), "Non-Float Type Data");
^
Question
So, I wanted to understand why my first program is not working as it should be, whether there is bug in my code/understanding or it is something else. I hope the above data would be sufficient to understand my question. However if anyone wants some additional data, please let me know.
The issue is here:
template <class InputIterator, class Function>
Function my_for_each(InputIterator first, InputIterator last, Function f)
invoked via:
my_for_each(vec_x.begin(), vec_x.end(), display);
This deduces Function (of my_for_each) to be a function pointer; for
void display(int& val)
the deduced type is void(*)(int&). The type trait std::is_function however checks if the passed type is a function type, not a function pointer type.
One solution is to remove the pointer:
template<typename T>
constexpr bool Is_valid_function(T& a) {
return std::is_function<typename std::remove_pointer<T>::type>::value;
}
But, as clang++ reveals, this still isn't sufficient:
template<typename T>
void check_valid_function(T& a) {
static_assert(Is_valid_function(a), "Not The Valid Function");
}
a as a function parameter (even if check_valid_function was constexpr!) is not a compile-time constant, therefore it may not appear in a constant expression (inside the function body). Hence, Is_valid_function(a) may not appear as the check for the static_assert. It might be possible to use something similar to declval, e.g.
static_assert(Is_valid_function( declval<T&>() ), "Not The Valid Function");
but unfortunately, declval is not constexpr, and I don't know how to write a constexpr version. So, you could pass a pointer instead:
static_assert(Is_valid_function(static_cast<T*>(nullptr)),
"Not a valid function");
For this, you need to rewrite Is_valid_function as follows:
template<typename T>
constexpr bool Is_valid_function(T*) {
return std::is_function<typename std::remove_pointer<T>::type>::value;
}
Note: the passed argument here is a pointer to a pointer to a function, but the parameter T* deduced T to be a pointer to a function, as before (hence the change in the signature). You might want to reflect that in the function name, if you choose this solution.
Other issues:
Relying on ADL for Standard Library algorithms
return for_each(first, last, f) ;
As far as I can see, this relies on ADL. But the iterators (and the function) are not required to be in namespace std (even for vector::iterator etc.), so you shouldn't rely on ADL:
return std::for_each(first, last, f);
Use of non-const refs for functions that don't need to modify their arguments, e.g.
constexpr bool Is_valid_function(T& a)
If you don't need to modify an argument, you should either pass it by value or by const reference, e.g.
constexpr bool Is_valid_function(T const& a)
"Wrong check" If this code is just for educational purposes, this isn't an issue. However, the check if the passed argument is of a function type is the "wrong check" when trying to check if the argument valid for a Standard Library algorithm. You should rather check whether f(*first) is well-formed. This allows for function objects and checks if the argument type is "valid".
This used to work some weeks ago:
template <typename T, T t>
T tfunc()
{
return t + 10;
}
template <typename T>
constexpr T func(T t)
{
return tfunc<T, t>();
}
int main()
{
std::cout << func(10) << std::endl;
return 0;
}
But now g++ -std=c++0x says:
main.cpp: In function ‘constexpr T func(T) [with T = int]’:
main.cpp:29:25: instantiated from here
main.cpp:24:24: error: no matching function for call to ‘tfunc()’
main.cpp:24:24: note: candidate is:
main.cpp:16:14: note: template<class T, T t> T tfunc()
main.cpp:25:1: warning: control reaches end of non-void function [-Wreturn-type]
clang++ -std=c++11 says that template's parameters of tfunc<T, t>() are ignored because invalid.
Is that a bug, or a fix ?
PS:
g++ --version => g++ (GCC) 4.6.2 20120120 (prerelease)
clang++ --version => clang version 3.0 (tags/RELEASE_30/final) (3.0.1)
The parameter t is not a constant expression. Hence the error. It should be also noted that it cannot be a constant expression.
You can pass the constant expression as argument, but inside the function, the object (the parameter) which holds the value, is not a constant expression.
Since t is not a constant expression, it cannot be used as template argument:
return tfunc<T, t>(); //the second argument must be a constant expression
Maybe, you want something like this:
template <typename T, T t>
T tfunc()
{
return t + 10;
}
template <typename T, T t> //<---- t became template argument!
constexpr T func()
{
return tfunc<T, t>();
}
#define FUNC(a) func<decltype(a),a>()
int main()
{
std::cout << FUNC(10) << std::endl;
}
Now it should work : online demo
I get the feeling that constexpr must also be valid in a 'runtime' context, not just at compile-time. Marking a function as constexpr encourages the compiler to try to evaluate it at compile-time, but the function must still have a valid run-time implementation.
In practice, this means that the compiler doesn't know how to implement this function at runtime:
template <typename T>
constexpr T func(T t)
{
return tfunc<T, t>();
}
A workaround is to change the constructor such that it takes its t parameter as a normal parameter, not as a template parameter, and mark the constructor as constexpr:
template <typename T>
constexpr T tfunc(T t)
{
return t + 10;
}
template <typename T>
constexpr T func(T t)
{
return tfunc<T>(t);
}
There are three levels of 'constant-expression-ness':
template int parameter, or (non-VLA) array size // Something that must be a constant-expression
constexpr // Something that may be a constant-expression
non-constant-expression
You can't really convert items that are low in that list into something that is high in that list, but obviously the other route it possible.
For example, a call to this function
constexpr int foo(int x) { return x+1; }
isn't necessarily a constant-expression.
// g++-4.6 used in these few lines. ideone doesn't like this code. I don't know why
int array[foo(3)]; // this is OK
int c = getchar();
int array[foo(c)]; // this will not compile (without VLAs)
So the return value from a constexpr function is a constant expression only if all the parameters, and the implementation of the function, can be completed at executed at compile-time.
Recap the question: You have two functions which take a parameter of type T. One takes its parameter as a template parameter, and the other as a 'normal' parameter.
I'm going to call the two functions funcT and funcN instead of tfunc and func.
You wish to be able to call funcT from funcN. Marking the latter as a constexpr doesn't help.
Any function marked as constexpr must be compilable as if the constexpr wasn't there. constexpr functions are a little schizophrenic. They only graduate to full constant-expressions in certain circumstances.
It would not be possible to implement funcN to run at runtime in a simple way, as it would need to be able to work for all possible values of t. This would require the compiler to instantiate many instances of tfunc, one for each value of t. But you can work around this if you're willing to live with a small subset of T. There is a template-recursion limit of 1024 in g++, so you can easily handle 1024 values of T with this code:
#include<iostream>
#include<functional>
#include<array>
using namespace std;
template <typename T, T t>
constexpr T funcT() {
return t + 10;
}
template<typename T, T u>
constexpr T worker (T t) {
return t==0 ? funcT<T,u>() : worker<T, u+1>(t-1);
}
template<>
constexpr int worker<int,1000> (int ) {
return -1;
}
template <typename T>
constexpr T funcN(T t)
{
return t<1000 ? worker<T,0>(t) : -1;
}
int main()
{
std::cout << funcN(10) << std::endl;
array<int, funcN(10)> a; // to verify that funcN(10) returns a constant-expression
return 0;
}
It uses a function worker which will recursively convert the 'normal' parameter t into a template parameter u, which it then uses to instantiate and execute tfunc<T,u>.
The crucial line is return funcT<T,u>() : worker<T, u+1>(t-1);
This has limitations. If you want to use long, or other integral types, you'll have to add another specialization. Obviously, this code only works for t between 0 and 1000 - the exact upper limit is probably compiler-dependent. Another option might be to use a binary search of sorts, with a different worker function for each power of 2:
template<typename T, T u>
constexpr T worker4096 (T t) {
return t>=4096 ? worker2048<T, u+4096>(t-4096) : worker2048<T, u>(t);
}
I think this will work around the template-recursion-limit, but it will still require a very large number of instantiations and would make compilation very slow, if it works at all.
Looks like it should give an error - it has no way of knowing that you passed in a constant value as t to func.
More generally, you can't use runtime values as template arguments. Templates are inherently a compile-time construct.
Is it a bug in Visual C++ 2010 or right behaviour?
template<class T>
T f(T const &r)
{
return r;
}
template<class T>
T f(T &&r)
{
static_assert(false, "no way"); //< line # 10
return r;
}
int main()
{
int y = 4;
f(y); //< line # 17
}
I thought, the function f(T &&) should never be called but it's called with T = int &. The output:
main.cpp(10): error C2338: no way
main.cpp(17) : see reference to function template instantiation 'T f(T)' being compiled
with
[
T=int &
]
Update 1 Do you know any C++x0 compiler as a reference? I've tried comeau online test-drive but could not compile r-value reference.
Update 2 Workaround (using SFINAE):
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_reference.hpp>
template<class T>
T f(T &r)
{
return r;
}
template<class T>
typename ::boost::disable_if< ::boost::is_reference<T>, T>::type f(T &&r)
{
static_assert(false, "no way");
return r;
}
int main()
{
int y = 4;
f(y);
// f(5); // generates "no way" error, as expected.
}
Update 3 Some of compilers trigger on static_assert(false, "no way") even if no function template instantiation. Workaround (thanks to #Johannes Schaub - litb)
template<class T> struct false_ { static bool const value = false; };
...
static_assert(false_<T>::value, "no way");
or
static_assert(sizeof(T) == sizeof(T), "no way");
As I understand it (and I may not be completely right; the specification is a bit complicated), the template type deduction rules conspire against you.
The compiler first attempts to substitute all templates (it's not choosing at this point yet—just looking for options) and gets:
T const &r matches int lvalue with T = int, creating f(int const &)
T &&r matches int lvalue with T = int& and int & && reduces to int&, creating f(int &) (there are rules saying this in the spec).
Now it comes to selecting correct overload and the later is better match, because the first differs in cv-qualification and the later does not. That's also the reason why when you remove the const, you get ambiguous overload error—the overloads end up being exactly the same.
Ad Update1: gcc supports many of the C++0x features. You can get native windows build from mingw or use cygwin.
Ad Update2: If you really need separate overloads for rvalue and lvalue, that seems to be the only option. But most templates do the right thing with just any kind of reference, perhaps using std::forward to ensure proper resolution of functions they call depending on whether they got rvalue or lvalue).
Your fix doesn't solve the problem with static_assert firing though. The static_assert(false, ...) will still trigger for compilers that parse templates at definition time (most do).
They will see that any function template instantiation will be ill-formed, and the Standard allows them to issue an error for the template itself then, and most will do so.
For making this work you need to make the expression dependent so that the compiler doesn't know when it parses the template that it will always evaluate to false. For example
template<class> struct false_ { static bool const value = false; };
template<class T>
T f(T &&r)
{
static_assert(false_<T>::value, "no way"); //< line # 10
return r;
}