After years of coding in c++, today I was asked a simple question, but indeed I couldn't find its answer and so here I am guys.
Besides wondering why this error is happening, I want to know how I can solve below error by modifying just the template function (without changing the main() function)
template <class T>
T Add(T first, T second)
{
return first + second;
}
int main()
{
auto sample_1 = Add(1, 2); // Works
auto sample_2 = Add(1.f, 2.f); // Works
auto sample_3 = Add(1.f, 2); // Error: no instance matches the argument types: (double, int)
return 0;
}
Besides wondering why this error is happening,
When you call Add(1.f, 2), the first argument type is float and the second argument type is int.
The compiler has to convert either the first argument to an int or the second argument to a float. Since both of them will require a conversion, they are equally good candidates. One cannot be preferred over the other.
I want to know how I can solve below error by modifying just the template function
You can change the function template to:
template <class T1, class T2>
auto Add(T1 first, T2 second)
{
return first + second;
}
or to (thanks #PiotrSkotnicki):
template <class T>
T Add(T first, decltype(first) second)
{
return first + second;
}
In this case, the type of second is not deduced from the argument being passed to the function. Type of first is deduced from the first argument and the type of second is forced to be the same as the type of first.
Add(1.2f, 2); // The first argument is deduced to be float
// The second argument is forced to be float.
Add(2, 1.2f); // The first argument is deduced to be int
// The second argument is forced to be int.
Just do:
template <class T1, class T2>
auto Add(T1 first, T2 second)
{
return first + second;
}
As with unique T, it is deduced once as int, once as double...
When you have
template <class T>
T Add(T first, T second)
the type of first and second need to be the same. If you want to take two different types then you can add a second template parameter
template <class T1, class T2>
auto Add(T1 first, T2 second)
or for C++11
template <class T1, class T2>
auto Add(T1 first, T2 second) -> decltype(first + second)
The compiler is trying to deduce the template type that it can use to create a function that matches the signature. Since the parameters are different types, it's unable to do so.
You could specify the type explicitly:
auto sample_3 = Add<float>(1.f, 2);
But you say you don't want to do that.
You can change the function to take two template types:
template <class T1, class T2>
T1 Add(T1 first, T2 second)
{
T1 p;
p = first + second;
return p;
}
But now you'll have to make an assumption about which type to return.
I've never tried to use auto as the return type, but apparently it works: http://ideone.com/1qO95w
template <class T1, class T2>
auto Add(T1 first, T2 second)
{
auto p = first + second;
return p;
}
Why write your own function when the standard already provided them?
In c++11, you can use:
#include <functional>
int main()
{
auto sample_1 = std::plus<float> () ( 1, 2 ); // Works
auto sample_2 = std::plus<float> () ( 1.f, 2.f ); // Works
auto sample_3 = std::plus<float> () ( 1.f, 2 ); // Works
return 0;
}
In c++14:
#include <functional>
int main()
{
auto sample_1 = std::plus<> () ( 1, 2 ); // Works
auto sample_2 = std::plus<> () ( 1.f, 2.f ); // Works
auto sample_3 = std::plus<> () ( 1.f, 2 ); // Works
return 0;
}
I want to know how I can solve below error by modifying just the template function
Like that:
template <class T1, class T2>
T1 Add(T1 first, T2 second)
{
T1 p;
p = first + second;
return p;
}
int main()
{
auto sample_1 = Add(1, 2);
auto sample_2 = Add(1.f, 2.f);
auto sample_3 = Add(1.f, 2);
return 0;
}
Related
Take the following code, which is a simplified example:
template <typename F>
void foo(F f) {
//bool some = is_variadic_v<F>; // Scenario #1
bool some = true; // Scenario #2
f(int(some), int(some));
}
int main() {
auto some = [](int i, int j) {
std::cout << i << " " << j << '\n';
};
foo([&some](auto... params) {
some(params...);
});
}
A function takes a generic variadic lambda and calls it with a fixed set of arguments. This lambda itself then just calls another function/lambda with a matching prototype.
As one could expect, in scenario 2, when f is called inside foo, the compiler will deduce params... to be the parameter pack {1, 1}.
For scenario #1, I am using a code from another Q&A to deduce the arity of a callable object. If however such an object is callable with more than a pre-defined maximum amount of arguments, it is considered "variadic". In detail, is_variadic_v will employ a form of expression SFINAE where it is attempted to call the function object with a decreasing number of arguments having an "arbitrary type" that is implictly convertible to anything.
The problem is now that apparently, the compiler will deduce F (and along its argument pack) during this metacode, and if it is variadic (such as in this case), it deduces F as a lambda taking the dummy arguments, i.e. something like main()::lambda(<arbitrary_type<0>, arbitrary_type<1>, arbitrary_type<2>, ..., arbitrary_type<N>>) if N is the "variadic limit" from above. Now params... is deduced as arbitrary_type<1>, arbitrary_type<2>, ... and correspondingly, the call some(params...) will fail.
This behaviour can be demonstrated in this little code example:
#include <utility>
#include <type_traits>
#include <iostream>
constexpr int max_arity = 12; // if a function takes more arguments than that, it will be considered variadic
struct variadic_type { };
// it is templated, to be able to create a
// "sequence" of arbitrary_t's of given size and
// hence, to 'simulate' an arbitrary function signature.
template <auto>
struct arbitrary_type {
// this type casts implicitly to anything,
// thus, it can represent an arbitrary type.
template <typename T>
operator T&&();
template <typename T>
operator T&();
};
template <
typename F, auto ...Ints,
typename = decltype(std::declval<F>()(arbitrary_type<Ints>{ }...))
>
constexpr auto test_signature(std::index_sequence<Ints...> s) {
return std::integral_constant<int, size(s)>{ };
}
template <auto I, typename F>
constexpr auto arity_impl(int) -> decltype(test_signature<F>(std::make_index_sequence<I>{ })) {
return { };
}
template <auto I, typename F, typename = std::enable_if_t<(I > 0)>>
constexpr auto arity_impl(...) {
// try the int overload which will only work,
// if F takes I-1 arguments. Otherwise this
// overload will be selected and we'll try it
// with one element less.
return arity_impl<I - 1, F>(0);
}
template <typename F, auto MaxArity>
constexpr auto arity_impl() {
// start checking function signatures with max_arity + 1 elements
constexpr auto tmp = arity_impl<MaxArity+1, F>(0);
if constexpr (tmp == MaxArity+1)
return variadic_type{ }; // if that works, F is considered variadic
else return tmp; // if not, tmp will be the correct arity of F
}
template <typename F, auto MaxArity = max_arity>
constexpr auto arity(F&&) { return arity_impl<std::decay_t<F>, MaxArity>(); }
template <typename F, auto MaxArity = max_arity>
constexpr auto arity_v = arity_impl<std::decay_t<F>, MaxArity>();
template <typename F, auto MaxArity = max_arity>
constexpr bool is_variadic_v = std::is_same_v<std::decay_t<decltype(arity_v<F, MaxArity>)>, variadic_type>;
template <typename F>
void foo(F f) {
bool some = is_variadic_v<F>;
//bool some = true;
f(int(some), int(some));
}
int main() {
auto some = [](int i, int j) {
std::cout << i << " " << j << '\n';
};
foo([&some](auto... params) {
some(params...);
});
}
Can I prevent this behaviour? Can I force the compiler to re-deduce the parameter list?
EDIT:
An additional peculiarity is that the compiler seems to act kind of schizophrenic. When I change the contents of foo to
foo([&some](auto... params) {
// int foo = std::index_sequence<sizeof...(params)>{ };
std::cout << sizeof...(params) << '\n';
});
the compiler will create a program that will print 2 in this example. If however I include the commented line (which, as it makes no sense, should trigger a compiler diagnostic), I get confronted with
error: cannot convert 'std::index_sequence<13>' {aka 'std::integer_sequence<long unsigned int, 13>'} to 'int' in initialization
85 | int foo = std::index_sequence<sizeof...(params)>{ };
so does the compiler now deduces sizeof...(params) to be 2 and 13 at the same time? Or did he change his mind and chooses now 13 just because I added another statement into the lambda? Compilation will also fail if I instead choose a static_assert(2 == sizeof...(params));. So the compiler deduces sizeof...(params) == 2, except if I ask him whether he did deduce 2, because then he didn't.
Apparently, it is very decisive for the parameter pack deduction what is written inside the lambda. Is it just me or does this behaviour really look pathologic?
I created a class that takes a typename template variable and a parameter pack. In the next step i want to be able to pass two objects of that class to my function.
My main problem is, passing the template parameters and the objects correctly to be able to use my function.
My class implementation.
//auto as template parameter is for non-type parameter(c++17)
template <auto value> constexpr auto DIM = value;
//constexpr on values in header files(c++17)
inline constexpr auto const DIM3 = DIM <3>;
inline constexpr auto const DIM2 = DIM <2>;
enum Index : int {lower = 0, upper = 1};
template<int base, int exponent>
int constexpr pow(){
if constexpr(exponent == 0){
return 1;
}else{
return base * pow<base, exponent-1>();
}
}
template<int Size, typename T>
struct Array{
T array[Size];
Array(const T * a){
for(int i = 0; i < Size; i++){
array[i] = a[i];
}
}
};
//auto as template parameter is for non-type parameters(c++17)
template<typename T = double, auto ...IndicesN>
class MatrixND{
private:
const Array<pow<DIM3, sizeof...(IndicesN)>(), T> matrix;
public:
MatrixND(const T * arr): matrix(arr){}
template<auto ...args>
auto constexpr getElement(){
}
};
The function that takes MatrixND objects:
template<auto posT1, auto posT2, typename A, typename B>
auto constexpr function(const MatrixND<A> tensor1, const MatrixND<B> tensor2){
return 0;
}
I tried the following, but it throws an error message "no matching function call":
const double arrayc[27] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27};
auto matrix1 = new MatrixND<double, upper, lower, lower>(arrayc);
function<1,1, decltype(matrix1), decltype(matrix1)>(matrix1, matrix1);
The error message:
error: no matching function for call to ‘function<1, 1, MatrixND<double, (Index)1, (Index)0, (Index)0>*, MatrixND<double, (Index)1, (Index)0, (Index)0>*>(MatrixND<double, (Index)1, (Index)0, (Index)0>*&, MatrixND<double, (Index)1, (Index)0, (Index)0>*&)’
contraction<1,1, decltype(matrix1), decltype(matrix1)>(matrix1, matrix1);
You need to add the parameter packs to the template parameters of function. Using
template<auto posT1, auto posT2, typename A, typename B, auto ...AIndicesN, auto ...BIndicesN>
auto constexpr function(const MatrixND<A, AIndicesN...> tensor1, const MatrixND<B, BIndicesN...> tensor2){
return 0;
}
Allows you to call function like
auto foo = function<1,1>(matrix1, matrix1);
Do note that for this to compile with your code you need to change
auto matrix1 = new MatrixND<double, upper, lower, lower>(arrayc);
to
auto matrix1 = MatrixND<double, upper, lower, lower>(arrayc);
since you don't actually want a pointer to a MatrixND but an actual MatrixND object.
When you call the function as
function<1,1, decltype(matrix1), decltype(matrix1)>(matrix1, matrix1);
you say that the template arguments A and B are decltype(matrix1), meaning they are really MatrixND<double, upper, lower, lower>.
That in turn means that e.g. the argument tensor1 will have the type const MatrixND<MatrixND<double, upper, lower, lower>>. Which is not what you pass.
A possible solution would be to not use MatrixND<A> (and MatrixND<B>) in the argument list, but only
template<auto posT1, auto posT2, typename A, typename B>
auto constexpr function(const A tensor1, const B tensor2){
return 0;
}
And you should probably pass references instead of values as arguments as well.
If you do like above with the function you also don't need the template arguments for the types of A and B as that would be deduced by the compiler:
function<1,1>(matrix1, matrix1);
When I use have template function which accepts another function as a parameter, C++ can't derive template parameters. It's very annoying to specify them all the time. How can I define the following function such that I don't have to specify type parameters every time?
#include <functional>
template <typename S, typename T>
T apply(const S& source, const function<T (const S&)>& f) {
return f(source);
}
template <typename S, class Functor, typename T>
T applyFun(const S& source, const Functor& f) {
return f(source);
}
int main() {
// Can't derive T. Why?
apply(1, [](int x) { return x + 1; });
// Compiles
apply<int, int>(1, [](const int& x) { return x + 1; });
// Can't derive T. Kind of expected.
applyFun(1, [](int x) { return x + 1; });
}
It makes sense to me why it can't derive type parameter in the second function, but not in the first one (since x + 1 is int, so it should deduce that T = int).
A template parameter must appear in a function parameter type to be deductible. Moreover lambdas are not functions so, whatsoever the return type of a lambda cannot participate to template argument deduction.
But in this case, there is no need to specify the return type. Return type deduction can do the job:
template <typename S, class Functor>
auto applyFun(const S& source, const Functor& f) {
return f(source);
}
If you can use C++17, you can use the deduction guides for std::function as follows
template <typename S, typename F,
typename T = typename decltype( std::function{std::declval<F>()} )::result_type>
T applyFun (S const & source, F const & f)
{
return f(source);
}
but, as pointed by Oliv, for your example function there is non need of T because you can use auto (from C++14; auto ... -> decltype(f(source)) in C++11).
-- EDIT --
The OP say
The good thing about this solution is that I can use T inside the function (e.g. if I want to implement vector_map).
You can detect and use T, also inside the function, using a using
Something as
template <typename S, typename F>
auto applyFun (S const & source, F const & f)
{
using T = typename decltype( std::function{f} )::result_type;
return f(source);
}
or simpler: using T = decltype( f(source) );.
The OP also observe that
The downside is that for some reason now I can't write [] (const auto& x) { ... } in function call.
Correct.
Because std::function template types can't be deduced from a generic-lambda.
But using the fact that you know the type of the argument, you can use decltype() again
template <typename S, typename F,
typename T = decltype(std::declval<F const>()(std::declval<S const>()))>
T applyFun (S const & source, F const & f)
{ return f(source); }
This solution should works also for C++14 and C++11.
I just watched Stephan T. Lavavej talk at CppCon 2018 on "Class Template Argument Deduction", where at some point he incidentally says:
In C++ type information almost never flows backwards ... I had to say "almost" because there's one or two cases, possibly more but very few.
Despite trying to figure out which cases he might be referring to, I couldn't come up with anything. Hence the question:
In which cases the C++17 standard mandates that type information propagate backwards?
Here is at least one case:
struct foo {
template<class T>
operator T() const {
std::cout << sizeof(T) << "\n";
return {};
}
};
if you do foo f; int x = f; double y = f;, type information will flow "backwards" to figure out what T is in operator T.
You can use this in a more advanced way:
template<class T>
struct tag_t {using type=T;};
template<class F>
struct deduce_return_t {
F f;
template<class T>
operator T()&&{ return std::forward<F>(f)(tag_t<T>{}); }
};
template<class F>
deduce_return_t(F&&)->deduce_return_t<F>;
template<class...Args>
auto construct_from( Args&&... args ) {
return deduce_return_t{ [&](auto ret){
using R=typename decltype(ret)::type;
return R{ std::forward<Args>(args)... };
}};
}
so now I can do
std::vector<int> v = construct_from( 1, 2, 3 );
and it works.
Of course, why not just do {1,2,3}? Well, {1,2,3} isn't an expression.
std::vector<std::vector<int>> v;
v.emplace_back( construct_from(1,2,3) );
which, admittedly, require a bit more wizardry: Live example. (I have to make the deduce return do a SFINAE check of F, then make the F be SFINAE friendly, and I have to block std::initializer_list in deduce_return_t operator T.)
Stephan T. Lavavej explained the case he was talking about in a tweet:
The case I was thinking of is where you can take the address of an overloaded/templated function and if it’s being used to initialize a variable of a specific type, that will disambiguate which one you want. (There’s a list of what disambiguates.)
we can see examples of this from cppreference page on Address of overloaded function, I have excepted a few below:
int f(int) { return 1; }
int f(double) { return 2; }
void g( int(&f1)(int), int(*f2)(double) ) {}
int main(){
g(f, f); // selects int f(int) for the 1st argument
// and int f(double) for the second
auto foo = []() -> int (*)(int) {
return f; // selects int f(int)
};
auto p = static_cast<int(*)(int)>(f); // selects int f(int)
}
Michael Park adds:
It's not limited to initializing a concrete type, either. It could also infer just from the number of arguments
and provides this live example:
void overload(int, int) {}
void overload(int, int, int) {}
template <typename T1, typename T2,
typename A1, typename A2>
void f(void (*)(T1, T2), A1&&, A2&&) {}
template <typename T1, typename T2, typename T3,
typename A1, typename A2, typename A3>
void f(void (*)(T1, T2, T3), A1&&, A2&&, A3&&) {}
int main () {
f(&overload, 1, 2);
}
which I elaborate a little more here.
I believe in static casting of overloaded functions the flow goes the opposite direction as in usual overload resolution. So one of those is backwards, I guess.
Why is it that optional parameters with template functions don't work in C++?
(Clarification: I'm hoping to understand why C++ was designed such that this wouldn't be possible.)
#include <iostream>
template<class T1, class T2> T1 inc(T1 v, T2 u = 1) { return v + u; }
int main() { std::cout << inc(5); }
prog.cpp: In function ‘int main()’: error: no matching function for call to ‘inc(int)’
You got it the wrong way round. Default arguments don't participate in argument deduction:
Argument deduction happens first, as part of selecting the desired overload, and then the default arguments of that overload are filled in if necessary.
What Kerrek SB said is correct, the compiler just doesn't have enough to deduce T2 from (by what it's allowed to do from the standard).
In this particular case you can probably fix it by using only one template argument for everything, i.e.
template< class T > T inc( const T v, const T u = 1 ) { return v + u; }
Default arguments do not participate in the deduction process (only to overload resolution, and the rules are very difficult to remember -- always keep it simple).
To achieve what you want, you can provide an additional overload:
template <class T1, class T2> T1 inc(T1 v, T2 u) { return v + u; }
template <class T> T inc(T v) { return v + T(1); }
Template functions in C++ are generated at compile time, and are only generated if they are needed. So you could get a function generated like:
inc( int, int );
Which would be the T1 = int and T2 = int version. The compiler can implicitly determine the type if you pass in parameters for every template argument, so:
int a = 1;
int b = 2;
inc( a, b );
The compiler could generate a function like the one above at compile time, because it can infer that T1 = int and T2 = int. If however you do what you are doing:
inc( 5 );
The compiler can determine that T1 = int, but it cannot determine what T2 is. So no function is generated, and you get the error about the function not existing. This may be fixable if you use one template parameter:
template<class T> T inc(T v, T u = 1) { return v + u; }
Or if you provide an overload:
template<class T> T inc(T v) { return v + 1; }
There is also a third way:
inc<int, int>( 5 );
But I guess this isn't what you want...