I tried to write this function with a default template argument:
template<typename A, typename B>
void func(int i1, int i2, A a, B b = 123){
...
}
In my mind I can call it like this: func(1, 2, 3) and compiler should deduce type B as int from default value, but I get no instance of overloaded function.
Is it incorrect C++ construction and compiler can't deduce type in this case?
The type of a template parameter in a function can't be deduced from a default argument. As shown in the example on cppreference.com:
Type template parameter cannot be deduced from the type of a function
default argument:
template<typename T> void f(T = 5, T = 7);
void g()
{
f(1); // OK: calls f<int>(1, 7)
f(); // error: cannot deduce T
f<int>(); // OK: calls f<int>(5, 7)
}
However, you can specify a default argument for the template parameter:
template<typename A, typename B = int>
void func(int i1, int i2, A a, B b = 123){
...
}
As often when default arguments don't work you can use overloads:
template<typename A, typename B>
void func(int i1, int i2, A a, B b){
...
}
template<typename A>
void func(int i1, int i2, A a){
func(i1,i2,a,123);
}
Related
I tried to write this function with a default template argument:
template<typename A, typename B>
void func(int i1, int i2, A a, B b = 123){
...
}
In my mind I can call it like this: func(1, 2, 3) and compiler should deduce type B as int from default value, but I get no instance of overloaded function.
Is it incorrect C++ construction and compiler can't deduce type in this case?
The type of a template parameter in a function can't be deduced from a default argument. As shown in the example on cppreference.com:
Type template parameter cannot be deduced from the type of a function
default argument:
template<typename T> void f(T = 5, T = 7);
void g()
{
f(1); // OK: calls f<int>(1, 7)
f(); // error: cannot deduce T
f<int>(); // OK: calls f<int>(5, 7)
}
However, you can specify a default argument for the template parameter:
template<typename A, typename B = int>
void func(int i1, int i2, A a, B b = 123){
...
}
As often when default arguments don't work you can use overloads:
template<typename A, typename B>
void func(int i1, int i2, A a, B b){
...
}
template<typename A>
void func(int i1, int i2, A a){
func(i1,i2,a,123);
}
This is the code I currently have:
class Foo
{
public:
template<typename T, typename... Args>
void Function(T t1, Args... args){
// Definition
}
private:
template<typename T>
void Function(T t1){
// Definition
}
};
#include "header.h"
int main()
{
Foo foo;
foo.Function(1, 2, 3, 4, 5);
return 0;
}
Works just fine. When I try to separate the definition to source.cpp, the gcc starts complaining. I know I have to specialize the templates in order to separate the definition, so I tried adding the code below to the header file:
template<>
void Foo::Function<int, int...>(int t1, int... args);
template<>
void Foo::Function<int>(int);
but without success. What am I missing
edit: gcc error messages:
header.h:15:28: error: expansion pattern ‘int’ contains no argument
packs void Foo::Function(int t1, int... args);
header.h:15:48: error: expansion pattern ‘int’ contains no argument
packs void Foo::Function(int t1, int... args);
You can't use int... as a parameter pack, and so this doesn't work. In addition, to separate the source from the definition, you have to fully specify the template, so int... wouldn't work even if that syntax were allowed.
Ways to get around this.
1. Make Function accept an initializer list.
We can write function so that it accepts an initializer list of ints:
#include <initializer_list>
class Foo {
public:
void Function(int t1, std::initializer_list<int> t2);
};
void Foo::Function(int t1, std::initializer_list<int> t2) {
for(int i : t2) {
// stuff
}
}
Now, you can call Function pretty easily, and it's not even templated:
Foo f;
f.Function(10, {1, 2, 3, 4, 5});
If there are other places you're using templates, you can expand a parameter pack directly into the initializer list:
template<class... Args>
void invoke_foo(Foo& f, int first, Args... rest) {
f.Function(first, {rest...});
}
2. Use SFINAE to disable all non-int overloads. We can disable all overloads of Foo::Function that don't only accept ints
#include <type_traits>
class Foo {
public:
// Requires C++17 for std::conjunction
// You could write your own std::conjunction too if you'd prefer
template<class... Args>
auto Function(int t1, Args... t2)
-> std::enable_if_t<std::conjunction<std::is_same<Args, int>...>::value>
{
// stuff
}
};
The downside to this is that non-integral values won't automatically be converted to int.
There is better way to do it.
First of all it looks like you want to force same type of all arguments (this is done by std::initializer_list in accepted answer). This can be foreced by providing extra explicit argument:
class Foo
{
public:
template<typename T, typename... Args>
void Function(T t1, T t2, Args... args)
{
LOG;
this->Function(t1);
this->Function(t2, args...);
}
private:
template<typename T>
void Function(T t1)
{
LOG << VAR(t1);
}
};
template<>
void Foo::Function<int>(int x)
{
LOG << " Spec" << VAR(x);
}
As you can see it is enough if you provide specialization of method for single argument.
Live demo
I'm trying to check if a (member) function has a default assignment for it's function arguments. But I can't seem to find a type trait that gives me that information. (something like std::has_default_assignment).
Demonstration (non functional) of what I'm trying to do.
struct TypeA {
void process(int a, int b) const {};
};
struct TypeB {
void process(int a, int b = 0) const {};
};
template<typename T, typename A1, typename A2>
using MemFcn = void(T::*)(A1, A2) const;
#include <type_traits>
template<typename T, typename A1, typename A2>
typename std::enable_if<std::has_default_assignment<A2>::value>::type
TestProcess(MemFcn<T, A1, A2> fcn) {
fcn(1);
};
template<typename T, typename A1, typename A2>
typename std::enable_if<!std::has_default_assignment<A2>::value>::type
TestProcess(MemFcn<T, A1, A2> fcn) {
fcn(1, 2);
};
template<typename T>
void TestConcepts(T)
{
TestProcess(&T::process);
}
int main(void) {
// Should call TypeA::process(1,2);
TestConcepts(TypeA{});
// Should call TypeB::process(1)
TestConcepts(TypeB{});
return 0;
}
How can I detect if a function parameter has a default assignment value?
A function pointer does not store the information of default arguments. Even if the function pointed to has default arguments, you will get compile time error saying wrong number of arguments.
You can however test if a specific member function can be called with different number of parameters. This does not guarantee that there is a default argument though, it could also be 2 different overloads that takes different number of parameters.
#include <type_traits>
#include <iostream>
struct TypeA {
void process(int a, int b) const {};
};
struct TypeB {
void process(int a, int b = 0) const {};
};
template <typename T, typename U = void>
struct has_default_arg : std::false_type {};
template <typename T>
struct has_default_arg<T, std::void_t<decltype(std::declval<T>().process(1))>> : std::true_type {};
template<typename T>
void TestProcess() {
if constexpr (has_default_arg<T>::value) {
std::cout << "default arg\n";
T{}.process(1);
}
else {
std::cout << "no default arg\n";
T{}.process(1,2);
}
};
template<typename T>
void TestConcepts(T)
{
TestProcess<T>();
}
int main(void) {
// Should call TypeA::process(1,2);
TestConcepts(TypeA{});
// Should call TypeB::process(1)
TestConcepts(TypeB{});
return 0;
}
This is using some c++17 features, but could be written in c++11 as well with some additional efforts.
Assume the following situation:
Type A and type B, B can be implicitly converted to A but the opposite is untrue.
I have a function
template<class T>
void do_stuff(T a, T b);
I want to call said function as such:
do_stuff(A{}, B{});
The problem here is that the compiler can't deduce the type and instead says:
template argument deduction/substitution failed
I can call my function like this:
do_stuff<A>(A{}, B{});
But this is more annoying for the user.
Alternatively I can do something like this:
template<class T, class M>
void do_stuff(T a, M b);
But then b goes on its merry way to be of type B (with the prior invocation).
Ideally I would like something like:
template<class T, class M = T>
void do_stuff(T a, M b);
Or:
template<class T#INSERT MAGIC SO THAT T IS DEDUCED AS BEING THE TYPE OF ARGUMENT NR 1#>
void do_stuff(T a, T b);
Is such a thing possible ?
Wrap b in a non-deduced context. That way, only a will be deduced and b must be converted to that type.
template <class T> struct dont_deduce { using type = T; };
template <class T> using dont_deduce_t = typename dont_deduce<T>::type;
template<class T>
void do_stuff(T a, dont_deduce_t<T> b);
There is answer in C++11: std::common_type
http://en.cppreference.com/w/cpp/types/common_type
template<typename A>
void f_impl(A a, A b)
{
}
template<typename A, typename B>
void f(A a, B b)
{
f_impl<typename std::common_type<A, B>::type>(a, b);
}
struct Z
{
};
struct W
{
operator Z();
};
int main()
{
f(1u, 1l); //work
f(W{}, Z{});
f(Z{}, W{}); //and this work too
}
https://godbolt.org/g/ieuHTS
It's certainly possible, just with a little delegation. You've made the problem pretty easy by specifying that you always want the inferred type to be the type of the first argument, so all we need to do is drop a little hint to the compiler.
template <class T>
void do_stuff_impl(T a, T b) {
cout << "Doing some work..." << endl;
}
template <class T, class S>
void do_stuff(T a, S b) {
do_stuff_impl<T>(a, b);
}
Now the user can call do_stuff with any two arguments, and C++ will try to implicitly cast the second argument to match the type of the first. If the cast isn't valid, you'll get a template instantiation error. On GCC, it says cannot convert ‘b’ (type ‘A’) to type ‘B’, which is pretty accurate and to the point. And any compiler worth its salt is going to be able to inline that delegated call, so there should be negligible overhead.
another way, seeking to express intent declaratively:
#include <type_traits>
// a B
struct B{};
// an A can be constructed from a B
struct A{
A() {};
A(B) {};
};
// prove that A is constructible from B
static_assert(std::is_convertible<B, A>::value, "");
// enable this function only if a U is convertible to a T
template<
// introduce the actors
class T, class U,
// declare intent
std::enable_if_t<std::is_convertible<U, T>::value>* = nullptr
>
void do_stuff(T, U)
{
}
int main()
{
// legal
do_stuff(A{}, B{});
// does not compile
// do_stuff(B{}, A{});
}
update:
to force the conversion, a lambda can be used:
// enable this function only if a U is convertible to a T
template<class T, class U,
std::enable_if_t<std::is_convertible<U, T>::value>* = nullptr
>
void do_stuff(T a, U b)
{
return[](T& a, T b) -> decltype(auto)
{
}(a, b);
}
Question is simple, how would I implement a function taking a variable number of arguments (alike the variadic template), however where all arguments have the same type, say int.
I was thinking about something alike this;
void func(int... Arguments)
Alternatively wont a recursive static assert on the types work?
A possible solution is to make the parameter type a container that can be initialized by a brace initializer list, such as std::initializer_list<int> or std::vector<int>. For example:
#include <iostream>
#include <initializer_list>
void func(std::initializer_list<int> a_args)
{
for (auto i: a_args) std::cout << i << '\n';
}
int main()
{
func({4, 7});
func({4, 7, 12, 14});
}
Here's a version that removes the function from the overload set, instead of giving a static_assert. This is allows you to provide other overloads of the function that could be used when the types aren't all the same, rather than a fatal static_assert that can't be avoided.
#include <type_traits>
template<typename... T>
struct all_same : std::false_type { };
template<>
struct all_same<> : std::true_type { };
template<typename T>
struct all_same<T> : std::true_type { };
template<typename T, typename... Ts>
struct all_same<T, T, Ts...> : all_same<T, Ts...> { };
template<typename... T>
typename std::enable_if<all_same<T...>::value, void>::type
func(T...)
{ }
If you want to support perfect forwarding you probably want to decay the types before checking them, so that the function will accept a mix of lvalue and rvalue arguments as long as they have the same type:
template<typename... T>
typename std::enable_if<all_same<typename std::decay<T>::type...>::value, void>::type
func(T&&...)
{ }
Alternatively, if you have a general purpose trait for testing the logical conjunction you can do it using std::is_same instead of writing your own all_same:
template<typename T, typename... Ts>
typename std::enable_if<and_<is_same<T, Ts>...>::value, void>::type
func(T&&, Ts&&...)
{ }
Because this requires at least one argument you'd also need another overload to support the zero-argument case:
void func() { }
The and_ helper can be defined like so:
template<typename...>
struct and_;
template<>
struct and_<>
: public std::true_type
{ };
template<typename B1>
struct and_<B1>
: public B1
{ };
template<typename B1, typename B2>
struct and_<B1, B2>
: public std::conditional<B1::value, B2, B1>::type
{ };
template<typename B1, typename B2, typename B3, typename... Bn>
struct and_<B1, B2, B3, Bn...>
: public std::conditional<B1::value, and_<B2, B3, Bn...>, B1>::type
{ };
I think you can do this by specifying a concrete type when chewing your arguments out of the argument pack. Something like:
class MyClass{};
class MyOtherClass{};
void func()
{
// do something
}
template< typename... Arguments >
void func( MyClass arg, Arguments ... args )
{
// do something with arg
func( args... );
// do something more with arg
}
void main()
{
MyClass a, b, c;
MyOtherClass d;
int i;
float f;
func( a, b, c ); // compiles fine
func( i, f, d ); // cannot convert
}
In the generic case void func( MyClass arg, Arguments ... args ) would become void func( arg, Arguments ... args ) with a template type T.
#Skeen
How about this?
template <typename T>
void func_1(std::initializer_list<T>&& a) {
// do something
}
template <typename... T>
void func(T&&... a) {
func_1({std::forward<T>(a)...});
}
int main() {
func(1, 2, 3);
// func(1, 2, 3, 4.0); // OK doesn't compile
}
If you don't want to use brace-based initializer_list/vector and want to keep the arguments separate in form of argument pack, then below solution checks it at compile time using recursive static_asserts:
#include<type_traits>
template<typename T1, typename T2, typename... Error>
struct is_same : std::false_type {};
template<typename T, typename... Checking>
struct is_same<T, T, Checking...> : is_same<T, Checking...> {};
template<typename T>
struct is_same<T,T> : std::true_type {};
template<typename... LeftMost>
void func (LeftMost&&... args)
{
static_assert(is_same<typename std::decay<LeftMost>::type...>::value,
"All types are not same as 'LeftMost'");
// ...
}
int main ()
{
int var = 2;
func(1,var,3,4,5); // ok
func(1,2,3,4.0,5); // error due to `static_assert` failure
}
Actually this solution would check all the arguments with respect to the first argument. Suppose it was double then everything would be checked against double.
Because I don't think I saw this solution, you could write a specific function for every type (in your case, just int) then a forwarding function taking variadic argument types.
Write each specific case:
then for each specific case:
// only int in your case
void func(int i){
std::cout << "int i = " << i << std::endl;
}
Then your forwarding function like this:
template<typename Arg0, typename Arg1 typename ... Args>
void func(Arg0 &&arg0, Arg1 &&arg1, Args &&... args){
func(std::forward<Arg0>(arg0));
func(std::forward<Arg1>(arg1), std::forward<Args>(args)...);
}
This is good because it is expandable for when you want to accept maybe another type too.
Used like this:
int main(){
func(1, 2, 3, 4); // works fine
func(1.0f, 2.0f, 3.0f, 4.0f); // compile error, no func(float)
}