Type deduction time - c++

I ran into this problem earlier today. In the following code:
template <int> struct Holder {};
template <typename> struct Helper { using T = Holder<__COUNTER__>; }; // ???
int main() {
auto a = typename Helper<bool>::T();
auto b = typename Helper<int>::T();
std::cout << (typeid(a) == typeid(b)) << std::endl;
return 0;
}
When compiled and executed with:
g++ test.cpp -std=c++11 -o test
./test
It prints out 1 instead of 0, meaning that the 2 Ts in Helper<int> and Helper<bool> are the same type, which makes me wonder:
Why the line marked with // ??? is executed only once instead of once for each of the type?
Is there a way to force the line to be executed once for each of the type and preferably without modifying the definition of Holder?
====================================================
Clarifications:
The (closer to) real scenario is:
The struct Holder is defined in a header from a third-party library. The type for the struct is actually very complicated and the library writer provides users with another macro:
template <bool, int> struct Holder {};
#define DEF_HOLDER(b) Holder<b, __COUNTER__>()
At some point of the program, I want to take a "snapshot" of the type with current counter by aliasing the type so that it could be used in a function:
template <bool b>
struct Helper { using T = decltype(DEF_HOLDER(b)); };
template <bool b, typename R = typename Helper<b>::T>
R Func() {
return R();
}
// Note that the following does not work:
// Since the 2 types generated by DEF_HOLDER do not match.
template <bool b>
auto Func() -> decltype(DEF_HOLDER(b)) {
return DEF_HOLDER(b);
}
The problem here is that the following 2 usage has inconsistent semantics as illustrated:
int main() {
auto a = DEF_HOLDER(true);
auto b = DEF_HOLDER(true);
auto c = Func<true>();
auto d = Func<true>();
std::cout << (typeid(a) == typeid(b)) << std::endl; // prints 0
std::cout << (typeid(c) == typeid(d)) << std::endl; // prints 1
return 0;
}
In my use case, it is important for multiple invocation of Func to return different types as it does with invoking DEF_HOLDER directly.

The symbol __COUNTER__ is a preprocessor macro, it's expanded once only.
That means T will always be Holder<0> (since __COUNTER__ starts at zero), no matter the type used for the template Helper.
See e.g. this GCC predefined macro reference for more information about __COUNTER__.

I am not sure whether I completely understand the problem, but since C++14 there is no need to use DEF_HOLDER two times. The following code also works:
template <bool b>
auto Func() {
return DEF_HOLDER(b);
}
If you want a different type for every function call, you can add the int parameter:
template <bool b, int i>
auto Func()
{
return Holder<b, i>();
}
You could hide this int in a macro:
#define FUNC(b) Func<b,__COUNTER__>();
Then a,b and c,d have the same semantics:
int main() {
auto a = DEF_HOLDER(true);
auto b = DEF_HOLDER(true);
auto c = FUNC(true);
auto d = FUNC(true);
std::cout << (typeid(a) == typeid(b)) << std::endl; // prints 0
std::cout << (typeid(c) == typeid(d)) << std::endl; // prints 0
return 0;
}

Related

False-branch of if constexpr not discarded in templated lambda

I have a problem with "if constexpr" in a templated lambda. For the sake of argument let's ignore how I got there, but I have a struct foo that is defined in some way to result in something as follows:
template<bool condition>
struct foo {
int a;
// Only contains b if condition is true
int b;
}
Now I can define a templated function thtemplate
template<bool condition>
void print_fun(foo & obj) {
/* Do something with obj.a */
if constexpr(condition)
/* Do something with obj.b */
};
Instantiating this function and using it will compile, if the constexpr parameter to foo is the same as the one to print_fun, i.e.
constexpr bool no = false;
foo<no> obj = {};
print_fun<no>(obj);
This does compile because the false branch is discarded inside a templated entity, and thus there is no problem with using obj.b inside print_fun.
However, if I define a similar lambda expression as follows:
template<bool condition>
auto print_lambda = [](foo & obj) {
/* Do something with obj.a */
if constexpr(condition)
/* Do something with obj.b */
};
and instantiate it:
constexpr bool no = false;
foo<no> obj = {};
print_lambda<no>(obj);
then the false branch is not discarded and the compiler gives me
'b': is not a member of 'foo'
Is this intended behavior, does it happen on other compilers?
Am I doing something wrong?
Or is it a bug in the compiler? (Microsoft Visual Studio Version 15.4.1, gcc 7.2)
Check out my test here with gcc, where it does not compile for a functor or function either.
Edit:
Here is the code of a my minimal example, I was not aware that the external link wouldn't suffice. This compiles on Visual Studio 15.4.1, except for the noted line.
foo_bar takes the place of foo in my description.
#include <iostream>
constexpr bool no = false;
struct foo {
int x;
};
struct bar {
int y;
};
template <bool, typename AlwaysTy, typename ConditionalTy>
struct Combined : AlwaysTy {};
template <typename AlwaysTy, typename ConditionalTy>
struct Combined<true, AlwaysTy, ConditionalTy> : AlwaysTy, ConditionalTy {};
using foo_bar = Combined<no, foo, bar>;
template<bool condition>
void print_fun(foo_bar & obj) {
std::cout << obj.x << std::endl;
if constexpr(condition)
std::cout << obj.y << std::endl;
};
template<bool condition>
auto print_lambda = [](foo_bar & obj) {
std::cout << obj.x << std::endl;
if constexpr(condition)
std::cout << obj.y << std::endl;
};
int main(int argc, char ** argv) {
foo_bar obj = {};
print_lambda<no>(obj); // Does not compile
print_fun<no>(obj);
}
According to the code linked,
template<bool condition>
void print_fun(foo_bar & obj) {
std::cout << obj.x << std::endl;
if constexpr(condition)
std::cout << obj.y << std::endl;
}
The problem is with if constexpr being used, the statement std::cout << obj.y << std::endl; is ill-formed for every possible instantiation of the template print_fun; i.e. no matter what's the value of condition it's just always ill-formed.
Note: the discarded statement can't be ill-formed for every possible specialization:
The common workaround for such a catch-all statement is a type-dependent expression that is always false:
To fix it you can make the statement to dependent on the template parameter, e.g.
template <bool condition>
using foo_bar = Combined<condition, foo, bar>;
template<bool condition>
void print_fun(foo_bar<condition> & obj) {
std::cout << obj.x << std::endl;
if constexpr(condition)
std::cout << obj.y << std::endl;
}
and use it as
foo_bar<no> obj = {};
print_fun<no>(obj);
Now for obj.y, obj is of type foo_bar<condition>, which depends on the template parameter condition.
LIVE

Is this considered valid c++11 or c++14? Or is gcc/clang getting it wrong?

While trying to solve Is it possible to tell if a class has hidden a base function in C++?, I generated this:
#include <type_traits>
#include <iostream>
#define ENABLE_IF(...) std::enable_if_t<(__VA_ARGS__), int> = 0
template<class T, class B, ENABLE_IF(std::is_same<void(T::*)(), decltype(&T::x)>::value)>
auto has_x_f(T*) -> std::true_type;
template<class T, class B>
auto has_x_f(B*) -> std::false_type;
template<class T, class B>
using has_x = decltype(has_x_f<T, B>((T*)nullptr));
template<typename T>
struct A
{
void x() {}
static const bool x_hidden;
template <typename R, ENABLE_IF(std::is_same<T, R>::value && x_hidden)>
void y(R value)
{
std::cout << "x() is hidden" << std::endl;
}
template <typename R, ENABLE_IF(std::is_same<T, R>::value && !x_hidden)>
void y(R value)
{
std::cout << "x() is not hidden" << std::endl;
}
//using t = std::integral_constant<bool, x_hidden>;
};
struct B : A<B>
{
void x() {}
};
struct C : A<C>
{
};
template<typename T>
const bool A<T>::x_hidden = has_x<T, A<T>>::value;
int main()
{
B b;
C c;
std::cout << "B: ";
std::cout << b.x_hidden << std::endl;
std::cout << "C: ";
std::cout << c.x_hidden << std::endl;
std::cout << "B: ";
b.y(b);
std::cout << "C: ";
c.y(c);
return 0;
}
Which outputs what I want:
B: 1
C: 0
B: x() is hidden
C: x() is not hidden
clang and gcc both compile and execute this "correctly", but vc++ doesn't (though I am aware that there are problems with it working properly with expressions similar to template <typename T> ... decltype(fn(std::declval<T>().mfn()))).
So my question is, is this considered valid or will it break later on? I'm also curious about the x_hidden being able to be used as a template parameter in the functions but not being able to use it in using t = std::integral_constant<bool, x_hidden>. Is that just because the template's type isn't fully declared at this point? If so, why did using it work for the function declarations?
If x_hidden is false, there is no template arguements for which this template function
template <typename R, ENABLE_IF(std::is_same<T, R>::value && x_hidden)>
void y(R value) {
std::cout << "x() is hidden" << std::endl;
}
can be instantiated, so your program is ill formed no diagnostic required. This is a common hack, its illegality may be made clear or even legal at some point.
There may be a reason for using has_x_f instead of just directly initializing is_hidden with the is_same clause, but it isn't demonstrated in your code.
For any template specialization, there must be arguments which would make the instantiation valid. If there are not, the program is ill-formed no diagnostic required.
I believe this clause is in the standard to permit compilers to do more advanced checks on templates, but not require them.
template <typename R, ENABLE_IF(std::is_same<T, R>::value && x_hidden)>
void y(R value)
{
std::cout << "x() is hidden" << std::endl;
}
the compiler is free to notice x_hidden is false, and say "it doesn't matter what is_same<T,R> is", and deduce that no template arguments could make this specialization valid. Then generate an error.
An easy hack is
template <class T2=T, class R,
ENABLE_IF(std::is_same<T2, R>::value && has_x<T2, A<T2>>::value)
>
void y(R value)
{
std::cout << "x() is hidden" << std::endl;
}
where we sneak another template argument in that equals T usually. Now, the compiler has to admit the possibility that T2 passes the has_x test, and that the passed argument is R. Users can bypass this by manually passing the "wrong" T2.
This may not solve everything. The standard is a bit tricky to read here, but one reading states that if within the body of y() we go and assume that our T itself has x(), we still violate the rule of the possibility of a valid template instantiation.
[temp.res] 14.6/8 (root and 1)
Knowing which names are type names allows the syntax of every template to be checked. The program is ill-formed, no diagnostic required, if:
no valid specialization can be generated for a template [...] and the template is not instantiated, or
No valid specialization for
template <typename R, ENABLE_IF(std::is_same<T, R>::value && x_hidden)>
void y(R value)
{
std::cout << "x() is hidden" << std::endl;
}
can be generated if x_hidden is false. The exitence of another overload is immaterial.
If you fix it using the T2 trick, the same rule holds if the body assumes T=T2.
Three are words in the standard that attempt to not cause the template to be instantiated in certain contexts, but I am unsure if that makes the above code well formed or not.
I tried compiling your code with the Intel C++ compiler(icpc (ICC) 17.0.2 20170213), and it would not compile with the following message:
main.cpp(30): error: expression must have a constant value
template <typename R, ENABLE_IF(std::is_same<T, R>::value && !x_hidden)>
^
/home/com/gcc/6.2.0/bin/../include/c++/6.2.0/type_traits(2512): error: class "std::enable_if<<error-constant>, int>" has no member "type"
using enable_if_t = typename enable_if<_Cond, _Tp>::type;
^
detected during instantiation of type "std::enable_if_t<<error-constant>, int>" at line 30 of "main.cpp"
main.cpp(62): error: more than one instance of overloaded function "B::y" matches the argument list:
function template "void A<T>::y(R) [with T=B]"
function template "void A<T>::y(R) [with T=B]"
argument types are: (B)
object type is: B
b.y(b);
I was however able to compile the following with both the Intel compiler and GCC.
#include <iostream>
#define ENABLE_IF(...) std::enable_if_t<(__VA_ARGS__), int> = 0
template<class T, class B, ENABLE_IF(std::is_same<void(T::*)(), decltype(&T::x)>::value)>
auto has_x_f(T*) -> std::true_type;
template<class T, class B>
auto has_x_f(B*) -> std::false_type;
template<class T, class B>
using has_x = decltype(has_x_f<T, B>((T*)nullptr));
template<class T>
class A
{
public:
T& self() { return static_cast<T&>(*this); }
void x() { }
template
< class TT = T
, typename std::enable_if<has_x<TT, A<TT> >::value, int>::type = 0
>
void y()
{
std::cout << " have x hidden " << std::endl;
// if you are so inclined, you can call x() in a "safe" way
this->self().x(); // Calls x() from class "Derived" (Here class B)
}
template
< class TT = T
, typename std::enable_if<!has_x<TT, A<TT> >::value, int>::type = 0
>
void y()
{
std::cout << " does not have x hidden " << std::endl;
// if you are so inclined, you can call x() in a "safe" way
this->self().x(); // Calls x() from class "Base" (Here class A)
}
};
class B : public A<B>
{
public:
void x() { }
};
class C : public A<C>
{
};
int main()
{
B b;
C c;
b.y();
c.y();
return 0;
}
I am not aware whether or not this is incorrect according to the standard however, but as I see it you do not run into the problem mentioned in one of the other answers, that you have a template that cannot be instantiated.
EDIT: I was able to get to compile on MSVC 2017 by some "old-times" template metaprogramming tricks, and using classes instead of functions.
If I use this implementation of has_x instead it compiles:
template<class T, bool>
struct has_x_impl;
template<class T>
struct has_x_impl<T, true>: std::true_type
{
};
template<class T>
struct has_x_impl<T, false>: std::false_type
{
};
template<class T>
using has_x = has_x_impl<T, std::is_same<void(T::*)(), decltype(&T::x)>::value>;
Full code on Wandbox here.
I had a bit of a code clean-up (got rid of the out-of-line x_hidden declaration) and ended up with the following. I also fixed it slightly based on #Yakk's answer above, to avoid [temp.res]/8 invalidating it.
#include <type_traits>
#include <iostream>
#include <cassert>
#define ENABLE_IF(...) std::enable_if_t<(__VA_ARGS__), int> = 0
template<class T, class Base, ENABLE_IF(std::is_same<void(T::*)(), decltype(&T::x)>::value)>
auto has_x_f() -> std::true_type;
template<class T, class Base, ENABLE_IF(std::is_same<void(Base::*)(), decltype(&T::x)>::value)>
auto has_x_f() -> std::false_type;
template<class T, class Base>
using has_x = decltype(has_x_f<T, Base>());
template<typename T>
struct A
{
void x() {}
static bool constexpr x_hidden() {
return has_x<T, A<T>>::value;
}
void y()
{
assert(x_hidden() == y_<T>(nullptr) );
}
void y2()
{
if constexpr(x_hidden()) {
typename T::BType i = 1;
(void)i;
} else {
typename T::CType i = 1;
(void)i;
}
}
private:
template <typename R, typename T2=T, ENABLE_IF(A<T2>::x_hidden())>
static bool y_(R*)
{
std::cout << "x() is hidden" << std::endl;
return true;
}
template <typename R, typename T2=T, ENABLE_IF(!A<R>::x_hidden())>
static bool y_(T*)
{
std::cout << "x() is not hidden" << std::endl;
return false;
}
};
struct B : A<B>
{
void x() {}
using BType = int;
};
static_assert(std::is_same<decltype(&B::x), void(B::*)()>::value, "B::x is a member of B");
struct C : A<C>
{
using CType = int;
};
static_assert(std::is_same<decltype(&C::x), void(A<C>::*)()>::value, "C::x is a member of A<C>");
int main()
{
B b;
C c;
std::cout << "B: ";
std::cout << B::x_hidden() << std::endl;
std::cout << "C: ";
std::cout << C::x_hidden() << std::endl;
std::cout << "B: ";
b.y();
b.y2();
std::cout << "C: ";
c.y();
c.y2();
return 0;
}
Live demo on wandbox -- gcc and clang are both happy with it.
MSVC 2017 complained
error C2064: term does not evaluate to a function taking 0 arguments
for both uses of A<T2>::x_hidden(), when instantiating A<B> for B to inherit from.
MSVC 2015 gave the same complaint, and then suffered an Internal Compiler Error. ^_^
So I think this is valid, but exercises MSVC's constexpr or template instantiation machinery in unpleasant ways.
Per the example in [expr.unary.op]/3, the type of &B::x is void (B::*)(), and the type of &C::x is void (A<C>::*)(). So the first has_x_f() will be present when T is B, and the second has_x_f() will be present when T is C and Base is A<C>.
Per [temp.inst]/2, instantiating the class instantiates declarations but not definitions of the members. Per [temp.inst]/3 and 4, member function definitions (including template functions) are not instantiated until required.
Our declarations here are currently different, as the use of R and T2 mean the compiler cannot determine the truth or falsehood of either size of the &&.
The use of the different parameter types helps MSVC, which would otherwise see them as redefinitions of the same template member template function. My reading of [temp.inst]/2 says this is not needed, as they're only redefintions when we instantiate them, and they cannot be instantiated at the same time. Because we use A<T2>::x_hidden() and !A<R>::x_hidden(), the compiler cannot know that they are mutually exclusive at this time. I don't think it's necessary to do that to avoid [temp.res]/8, simply using A<R>::x_hidden() seems safe-enough to me. This was also to ensure that in the two templates, R as actually used.
From there on, it's pretty easy. y() shows we have the right values coming from both paths.
Depend on your use-case, you could use if constexpr with x_hidden() to avoid all the template magic in y_(), per y2() above.
This avoids the issue with [temp.res]/8 described in #Yakk's answer, as the problematic clause [temp.res]/8.1 is that the template is ill-formed if
no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, [...]
So as long as you instantiate A<T>::y2() for some T, then you're not subject to this clause.
The y2() approach has the advantage of working with MSVC2017, as long as you pass in the "/std:c++latest" compiler flag.

How to use generic template function to handle objects with different members?

I have looked around a while for a solution to this, however, I might not know the exact definition or language syntax of what I am trying to accomplish, so I decided to post.
I have certain objects/structs like so:
struct A
{
char myChar;
bool hasArray = false;
};
template <uint8_t ARRAY_LEN>
struct AA : public A
{
hasArray = true;
uint8_t myArray[ARRAY_LEN];
};
I want to create a generic function that can take in both of these object types and to perform common work as well as specific work for the derived struct AA. Something like the following:
template <typename T>
void func(T (&m))
{
if (T.hasArray)
{
// do some processing with m.myArray
std::cout << sizeof(m.myArray) << std::endl;
// ...
}
// common processing
std::cout << "myChar: " << m.myChar << std::endl;
};
I want to be able to call the function like so:
A a;
AA aa;
func(a); // compiler error, this would not work as no array member
func(aa); // this works
Granted this is just an example that illustrates my intent, but it sums up what I would like to do. The actual code is a lot more complex and involved many more objects. I know I can overload, but I want to know if there is a way to do it with one generic function? Also note that I understand why the compiler complains with the sample code I would like to know if there is a workaround or some other c++ functionality that I am missing. I would not like to do any type casting...
- Using c++11 and GCC 4.8.5
This is a C++14 feature of reasonably large complexity. C++17 introduced if constexpr to make this easier; but it is doable.
template<std::size_t I>
using index_t=std::integral_constant<std::size_t, I>;
template<std::size_t I>
constexpr index_t<I> index{};
constexpr inline index_t<0> dispatch_index() { return {}; }
template<class B0, class...Bs,
std::enable_if_t<B0::value, int> =0
>
constexpr index_t<0> dispatch_index( B0, Bs... ) { return {}; }
template<class B0, class...Bs,
std::enable_if_t<!B0::value, int> =0
>
constexpr auto dispatch_index( B0, Bs... ) {
return index< 1 + dispatch_index( decltype(Bs){}...) >;
}
template<class...Bs>
auto dispatch( Bs... ) {
using I = decltype(dispatch_index( decltype(Bs){}... ));
return [](auto&&...args)->decltype(auto){
return std::get<I::value>( std::make_tuple(decltype(args)(args)..., [](auto&&...){}) );
};
}
dispatch( some_test ) returns a lambda that takes auto&&.... It in turn returns the first argument if some_test is of a true-like-type, and the second argument (or [](auto&&...){} if no second argument) if some_test is of a false-like-type.
We then write code to detect your myArray.
namespace details {
template<template<class...>class Z, class=void, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = typename details::can_apply<Z, void, Ts...>::type;
template<class T>
using myArray_type = decltype( std::declval<T>().myArray );
template<class T>
using has_myArray = can_apply< myArray_type, T >;
and has_myArray<T> is true-like if T has a member .myArray.
We hook these together
dispatch( has_myArray<T>{} )(
[&](auto&& m) {
// do some processing with m.myArray
std::cout << sizeof(m.myArray) << std::endl;
// ...
}
)( m );
and now the lambda in the middle is run if and only if m.myArray is valid.
More complex tests that check for more than just existence can be written, but the above is usually sufficient.
In a non-C++11 compiler like MSVC 2015, replace
std::enable_if_t<B0::value, int> =0
and
std::enable_if_t<!B0::value, int> =0
with
class = std::enable_if_t<B0::value>
and
class = std::enable_if_t<!B0::value>, class=void
respectively. Yes, these are uglier. Go talk to MSVC compiler team.
If your compiler lacks C++14, you'll have to write your own void_t and either write your own enable_if_t or use the ugly longer version using enable_if.
In addition, the template variable index is illegal in C++11. Replace index<blah> with index_t<blah>{}.
The lack of auto&& lambdas makes the above very painful; you may have to convert the lambda to an out-of-line function object. However, auto lambdas where one of the first C++14 features people implemented, often before they finished C++11.
The above code is solid designed, but may contain typos.
Overloading works just fine in your case if you don't want to modify your instances:
#include<iostream>
#include<cstdint>
struct A
{
char myChar;
};
template <uint8_t ARRAY_LEN>
struct AA : public A
{
uint8_t myArray[ARRAY_LEN];
};
void func(const A &m)
{
std::cout << "myChar: " << m.myChar << std::endl;
};
template <uint8_t AL>
void func(const AA<AL> &m)
{
std::cout << sizeof(m.myArray) << std::endl;
func(static_cast<const A &>(m));
}
int main() {
func(A{});
func(AA<1>{});
}
If you still want to go with a template function and a bit of sfinae, I would probably use something like this instead:
#include<iostream>
#include<cstdint>
struct A
{
char myChar;
};
template <uint8_t ARRAY_LEN>
struct AA : public A
{
uint8_t myArray[ARRAY_LEN];
};
void func(A &m)
{
std::cout << "myChar: " << m.myChar << std::endl;
}
template <typename T>
auto func(T &m) -> decltype(m.myArray, void())
{
std::cout << sizeof(m.myArray) << std::endl;
A &a = m;
func(a);
}
int main() {
AA<1> aa{};
A a{};
func(a);
func(aa);
}
Note that in both cases you don't actually require the hasArray member data.
there is a way to do it with one generic function?
I don't think so, because if you insert a sizeof(m.myArray) in this function, you can't call it with a type without a myArray member. Even if it is in a part of code that, run time, isn't executed, because the compiler need to compile it.
But, if I understand correctly, your hasArray say if your struct has a myArray member or not. So I suppose you can transform it in a static constexpr member, as follows
struct A
{
static constexpr bool hasArray { false };
char myChar { 'z' };
};
template <uint8_t ARRAY_LEN>
struct AA : public A
{
static constexpr bool hasArray { true };
uint8_t myArray[ARRAY_LEN];
};
Now, in func(), you can call a second function, func2(), to choose the two cases: myArray or not myArray. You can use SFINAE for this but (IMHO) is better tag dispatching, in this case. So you can transform your hasArray in a different type
template <typename T>
void func2 (T const & m, std::true_type const &)
{ std::cout << sizeof(m.myArray) << ", "; }
template <typename T>
void func2 (T const &, std::false_type const &)
{ }
template <typename T>
void func(T (&m))
{
func2(m, std::integral_constant<bool, T::hasArray>{});
// common processing
std::cout << "myChar: " << m.myChar << std::endl;
}
Now you can call func() with both types
int main()
{
A a;
AA<12U> aa;
func(a); // print myChar: z
func(aa); // print 12, myChar: z
}
Remember to include type_traits and iostream.

Specifying default parameter when calling C++ function

Suppose I have code like this:
void f(int a = 0, int b = 0, int c = 0)
{
//...Some Code...
}
As you can evidently see above with my code, the parameters a,b, and c have default parameter values of 0. Now take a look at my main function below:
int main()
{
//Here are 4 ways of calling the above function:
int a = 2;
int b = 3;
int c = -1;
f(a, b, c);
f(a, b);
f(a);
f();
//note the above parameters could be changed for the other variables
//as well.
}
Now I know that I can't just skip a parameter, and let it have the default value, because that value would evaluate as the parameter at that position. What I mean is, that I cannot, say call, f(a,c), because, c would be evaluated as b, which is what I don't want, especially if c is the wrong type. Is there a way for the calling function to specify in C++, to use whatever default parameter value there is for the function in any given position, without being limited to going backwards from the last parameter to none? Is there any reserved keyword to achieve this, or at least a work-around? An example I can give would be like:
f(a, def, c) //Where def would mean default.
There isn't a reserved word for this, and f(a,,c) is not valid either. You can omit a number of rightmost optional parameters, as you show, but not the middle one like that.
http://www.learncpp.com/cpp-tutorial/77-default-parameters/
Quoting directly from the link above:
Multiple default parameters
A function can have multiple default parameters:
void printValues(int x=10, int y=20, int z=30)
{
std::cout << "Values: " << x << " " << y << " " << z << '\n';
}
Given the following function calls:
printValues(1, 2, 3);
printValues(1, 2);
printValues(1);
printValues();
The following output is produced:
Values: 1 2 3
Values: 1 2 30
Values: 1 20 30
Values: 10 20 30
Note that it is impossible to supply a user-defined value for z
without also supplying a value for x and y. This is because C++ does
not support a function call syntax such as printValues(,,3). This has
two major consequences:
1) All default parameters must be the rightmost parameters. The
following is not allowed:
void printValue(int x=10, int y); // not allowed
2) If more than one default parameter exists, the leftmost default
parameter should be the one most likely to be explicitly set by the
user.
As workaround, you may (ab)use boost::optional (until std::optional from c++17):
void f(boost::optional<int> oa = boost::none,
boost::optional<int> ob = boost::none,
boost::optional<int> oc = boost::none)
{
int a = oa.value_or(0); // Real default value go here
int b = ob.value_or(0); // Real default value go here
int c = oc.value_or(0); // Real default value go here
//...Some Code...
}
and then call it
f(a, boost::none, c);
Not exactly what you asked for, but you can use std::bind() to fix a value for a parameter.
Something like
#include <functional>
void f(int a = 0, int b = 0, int c = 0)
{
//...Some Code...
}
int main()
{
// Here are 4 ways of calling the above function:
int a = 2;
int b = 3;
int c = -1;
f(a, b, c);
f(a, b);
f(a);
f();
// note the above parameters could be changed
// for the other variables as well.
using namespace std::placeholders; // for _1, _2
auto f1 = std::bind(f, _1, 0, _2);
f1(a, c); // call f(a, 0, c);
return 0;
}
With std::bind() you can fix values different from default parameters' values or values for parameters without default values.
Take into account that std::bind() is available only from C++11.
You already have an accepted answer, but here's another workaround (that - I believe - has advantages over the other proposed workarounds):
You can strong-type the arguments:
struct A { int value = 0; };
struct B { int value = 2; };
struct C { int value = 4; };
void f(A a = {}, B b = {}, C c = {}) {}
void f(A a, C c) {}
int main()
{
auto a = 0;
auto b = -5;
auto c = 1;
f(a, b, c);
f(a, C{2});
f({}, {}, 3);
}
Advantages:
it's simple and easy to maintain (one line per argument).
provides a natural point for constricting the API further (for example, "throw if B's value is negative").
it doesn't get in the way (works with default construction, works with intellisense/auto-complete/whatever as good as any other class)
it is self-documenting.
it's as fast as the native version.
Disadvantages:
increases name pollution (better put all this in a namespace).
while simple, it is still more code to maintain (than just defining the function directly).
it may raise a few eyebrows (consider adding a comment on why strong-typing is needed)
If all parameters of the function were of distinct types, you could find out which parameters were passed and which were not and choose the default value for the latter.
In order to achieve the distinct type requirement, you can wrap your parameters and pass it to a variadic function template.
Then even the order of the argument does not matter anymore:
#include <tuple>
#include <iostream>
#include <type_traits>
// -----
// from http://stackoverflow.com/a/25958302/678093
template <typename T, typename Tuple>
struct has_type;
template <typename T>
struct has_type<T, std::tuple<>> : std::false_type {};
template <typename T, typename U, typename... Ts>
struct has_type<T, std::tuple<U, Ts...>> : has_type<T, std::tuple<Ts...>> {};
template <typename T, typename... Ts>
struct has_type<T, std::tuple<T, Ts...>> : std::true_type {};
template <typename T, typename Tuple>
using tuple_contains_type = typename has_type<T, Tuple>::type;
//------
template <typename Tag, typename T, T def>
struct Value{
Value() : v(def){}
Value(T v) : v(v){}
T v;
};
using A = Value<struct A_, int, 1>;
using B = Value<struct B_, int, 2>;
using C = Value<struct C_, int, 3>;
template <typename T, typename Tuple>
std::enable_if_t<tuple_contains_type<T, Tuple>::value, T> getValueOrDefaultImpl(Tuple t)
{
return std::get<T>(t);
}
template <typename T, typename Tuple>
std::enable_if_t<!tuple_contains_type<T, Tuple>::value, T> getValueOrDefaultImpl(Tuple)
{
return T{};
}
template <typename InputTuple, typename... Params>
auto getValueOrDefault(std::tuple<Params...>, InputTuple t)
{
return std::make_tuple(getValueOrDefaultImpl<Params>(t)...);
}
template <typename... Params, typename ArgTuple>
auto getParams(ArgTuple argTuple)
{
using ParamTuple = std::tuple<Params...>;
ParamTuple allValues = getValueOrDefault(ParamTuple{}, argTuple);
return allValues;
}
template <typename... Args>
void f(Args ... args)
{
auto allParams = getParams<A,B,C>(std::make_tuple(args...));
std::cout << "a = " << std::get<A>(allParams).v << " b = " << std::get<B>(allParams).v << " c = " << std::get<C>(allParams).v << std::endl;
}
int main()
{
A a{10};
B b{100};
C c{1000};
f(a, b, c);
f(b, c, a);
f(a, b);
f(a);
f();
}
output
a = 10 b = 100 c = 1000
a = 10 b = 100 c = 1000
a = 10 b = 100 c = 3
a = 10 b = 2 c = 3
a = 1 b = 2 c = 3
live example
I will just use static functions to define default values that can change:
class defValsExample
{
public:
defValsExample() {
}
static int f1def_a() { return 1; }
static int f1def_b() { return 2; }
int f1(int a = f1def_a(), int b = f1def_b()) {
return a+b;
}
};
int main()
{
defValsExample t;
int c = t.f1(t.f1def_a(),4);
}

C++: template to check if expression compiles

When writing template specialization with SFINAE you often come to the point where you need to write a whole new specialization because of one small not-existing member or function. I would like to pack this selection into a small statement like orElse<T a,T b>.
small example:
template<typename T> int get(T& v){
return orElse<v.get(),0>();
}
is this possible?
The intent of orElse<v.get(),0>() is clear enough, but if such a thing could exist,
it would have to be be one of:
Invocation Lineup
orElse(v,&V::get,0)
orElse<V,&V::get>(v,0)
orElse<V,&V::get,0>(v)
where v is of type V, and the function template thus instantiated
would be respectively:
Function Template Lineup
template<typename T>
int orElse(T & obj, int(T::pmf*)(), int deflt);
template<typename T, int(T::*)()>
int orElse(T & obj, int deflt);
template<typename T, int(T::*)(), int Default>
int orElse(T & obj);
As you appreciate, no such a thing can exist with the effect that you want.
For any anyone who doesn't get that,
the reason is simply this: None of the function invocations in the Invocation Lineup
will compile if there is no such member as V::get. There's no getting round
that, and the fact that the function invoked might be an instantiation of a
function template in the Function Template Lineup makes no difference whatever.
If V::get does not exist, then any code that mentions it will not compile.
However, you seem to have a practical goal that need not be approached
in just this hopeless way. It looks as if, for a given name foo and an given type R,
you want to be able to write just one function template:
template<typename T, typename ...Args>
R foo(T && obj, Args &&... args);
which will return the value of R(T::foo), called upon obj with arguments args...,
if such a member function exists, and otherwise return some default R.
If that's right, it can be achieved as per the following illustration:
#include <utility>
#include <type_traits>
namespace detail {
template<typename T>
T default_ctor()
{
return T();
}
// SFINAE `R(T::get)` exists
template<typename T, typename R, R(Default)(), typename ...Args>
auto get_or_default(
T && obj,
Args &&... args) ->
std::enable_if_t<
std::is_same<R,decltype(obj.get(std::forward<Args>(args)...))
>::value,R>
{
return obj.get(std::forward<Args>(args)...);
}
// SFINAE `R(T::get)` does not exist
template<typename T, typename R, R(Default)(), typename ...Args>
R get_or_default(...)
{
return Default();
}
} //namespace detail
// This is your universal `int get(T,Args...)`
template<typename T, typename ...Args>
int get(T && obj, Args &&... args)
{
return detail::get_or_default<T&,int,detail::default_ctor>
(obj,std::forward<Args>(args)...);
}
// C++14, trivially adaptable for C++11
which can be tried out with:
#include <iostream>
using namespace std;
struct A
{
A(){};
int get() {
return 1;
}
int get(int i) const {
return i + i;
}
};
struct B
{
double get() {
return 2.2;
}
double get(double d) {
return d * d;
}
};
struct C{};
int main()
{
A const aconst;
A a;
B b;
C c;
cout << get(aconst) << endl; // expect 0
cout << get(a) << endl; // expect 1
cout << get(b) << endl; // expect 0
cout << get(c) << endl; // expect 0
cout << get(a,1) << endl; // expect 2
cout << get(b,2,2) << endl; // expect 0
cout << get(c,3) << endl; // expect 0
cout << get(A(),2) << endl; // expect 4
cout << get(B(),2,2) << endl; // expect 0
cout << get(C(),3) << endl; // expect 0
return 0;
}
There is "compound SFINAE" in play in the complicated return type:
std::enable_if_t<
std::is_same<R,decltype(obj.get(std::forward<Args>(args)...))
>::value,R>
If T::get does not exist then decltype(obj.get(std::forward<Args>(args)...)
does not compile. But if it does compile, and the return-type of T::get is
something other than R, then the std::enable_if_t type specifier does not
compile. Only if the member function exists and has the desired return type R
can the R(T::get) exists case be instantiated. Otherwise the
catch-all R(T::get) does not exist case is chosen.
Notice that get(aconst) returns 0 and not 1. That's as it should be,
because the non-const overload A::get() cannot be called on a const A.
You can use the same pattern for any other R foo(V & v,Args...) and
existent or non-existent R(V::foo)(Args...).
If R is not default-constructible, or if you want the default R that
is returned when R(V::foo) does not exist to be something different from
R(), then define a function detail::fallback (or whatever) that returns the
desired default R and specify it instead of detail::default_ctor
How nice it would be it you could further template-paramaterize the pattern
to accomodate any possible member function of T with any possible return
type R. But the additional template parameter you would need for that would
be R(T::*)(typename...),and its instantiating value would have to be
&V::get (or whatever), and then the pattern would
force you into the fatal snare of mentioning the thing whose existence is in doubt.
Yes, this is more or less possible. It is known as a "member detector". See this wikibooks link for how to accomplish this with macros. The actual implementation will depend on whether you are using pre- or post-C++11 and which compiler you are using.