Edit: This funcion have to check types one by one and return obj of any that will satisfy condition or nullptr.
template <typename... Args, typename = std::enable_if_t<(sizeof...(Args) == 0)>()>
std::nullptr_t f() { return nullptr; }
template <typename T, typename... Args>
BaseClassOfAllArgs* f() {
if (<some condition related to T...>)
return new T;
return f<Args...>();
}
This code works for me. But I wonder if is it possible to rewrite this code to use concept?
I mean something like this:
template <typename... Args>
concept NoArgs = (sizeof...(Args) == 0);
and then use it instead of std::enable_if(this code doesnt work)
template <NoArgs Args>
std::nullptr_t f() { return nullptr; }
template <typename T, typename... Args>
BaseClassOfAllArgs* f() {
if (<some condition related to T...>)
return new T;
return f<Args...>();
}
EDIT: Here is working example of code after taking some tips from
guys in comments. After I added 'Base' to the template it turns out that EmptyPack concept is no longer needed. And First template naturally needs 3 typenames. However, I am not sure about this concept EmptyPack. Is it really making my program ill-formed no diagnostic required?
#include <iostream>
#include <type_traits>
#include <typeinfo>
class X {};
class A : public X {};
class B : public X {};
class C : public X {};
class D : public C {};
class E {};
template<class T, class... Args >
concept DerivedsOfBase = (std::is_base_of_v<T, Args> && ...);
template<typename... Args>
concept EmptyPack = sizeof...(Args) == 0;
template<typename T>
std::nullptr_t f() {
std::cout << "End of the types list" << std::endl;
return nullptr;
}
template<typename Base, typename T, typename... Args> requires DerivedsOfBase<Base, T, Args...>
Base* f() {
std::cout << typeid(T).name() << std::endl;
if (<some condition related to T>)
return new T;
return f<Base, Args...>();
}
int main()
{
auto ptr = f<X, A, B, C>();
auto ptr2 = f<X, A, B, D>();
//auto ptr3 = f<X, A, B, E>(); // compile error
return 0;
}
Any template parameter pack that only has valid instantiations for packs of size 0 makes your program ill-formed, no diagnostic required.
This applies to your first "working" example, as well as any practical variant I can think of using concepts.
From the N3690 draft standard [temp.res] 14.6/8:
If every valid specialization of a variadic template requires an empty template parameter pack, the template is ill-formed, no diagnostic required.
(I've seen that in many versions of C++, I'm just using a random draft standard as it showed up when I googled C++ standard pdf.)
Note that (a) your two f are overloads not specializations, and (b) what C++ programmers mean by "valid specialization of a template" and what the standard means are not quite the same thing.
In essence, pretty much any attempt at your concept will result in ill-formed, no diagnostic required programs.
To see the problem here, we'll reword the standard using negation:
If (every valid specialization of a variadic template requires an empty template parameter pack) then (the template is ill-formed, no diagnostic required).
We can convert this "forall" into a "there exists". "Forall X, P(X)" is the same as "Not( There exists X, Not P(X) )".
Unless (there exists a valid specialization of a variadic template with a non-empty template parameter pack) then (the template is ill-formed, no diagnostic required).
If a variadic template has a requires clause that mandates the variadic template pack is empty, then no valid specialization of that template with an empty parameter pack exists. So your template is ill-formed, no diagnostic required.
In general, these kind of rules exist so that compilers can check if your code would always be nonsense. Things like templates where no type parameter could make it compile, or packs that must be empty, are generally a sign that your code has bugs. By making it ill-formed and not requiring a diagnostic, compilers are permitted to emit diagnostics and fail to compile.
An issue I have with the standard is that it is not just permitted to fail to compile, but it is permitted to compile a program that does literally anything.
Because the compiler is permitted to do this, and some optimizations in other cases actually result in this happening, you should avoid writing code that is ill-formed, no diagnostic required, like the plague.
A workaround is:
namespace implementation_details {
struct never_use_me;
}
template <class=implementation_details::never_use_me>
std::nullptr_t f() { return nullptr; }
template <typename T, typename... Args>
T* f() {
if (<some condition related to T...>)
return new T;
return f<Args...>();
}
another option is:
template <typename T, typename... Args>
T* f() {
if (<some condition related to T...>)
return new T;
if constexpr (sizeof...(Args)==0)
return nullptr;
else
return f<Args...>();
}
You don't really need concepts for that, simply use if constexpr:
T* f() {
if constexpr (sizeof...(Args) != 0) {
if (<some condition related to T...>)
return new T;
return f<Args...>();
} else {
return nullptr;
}
}
If you really want separated functions, you could always use overloading:
template <typename T> // no pack
T* f() {
return nullptr;
}
template <typename T, typename Arg1, typename... Args> // one of more Args
T* f() {
if (<some condition related to T...>)
return new T;
return f<Arg1, Args...>();
}
But of course you can always use requires expressions if you really want to keep the same logic you had with SFINAE. It should be possible (and enough) without a declared concept, but only with constraint:
template <typename T, typename...> // pack logically always empty
T* f() {
return nullptr;
}
template <typename T, typename... Args> requires (sizeof...(Args) > 0)
T* f() {
if (<some condition related to T...>)
return new T;
return f<Args...>();
}
Of course you can also wrap the condition inside a concept, but you gain very little using this technique in this case, since you still have to use the concept inside a require clause:
template<typename... Args>
concept nonempty_pack = sizeof...(Args) > 0;
template <typename T, typename...> // pack logically always empty
T* f() {
return nullptr;
}
template <typename T, typename... Args> requires nonempty_pack<Args...>
T* f() {
if (<some condition related to T...>)
return new T;
return f<Args...>();
}
The syntax template<my_concept T> will send T as the first parameter. You'll always get that parameter snet automatically, hence the need to put the concept in the requires clause.
Related
I was really excited when I first heard about C++20 constraints and concepts, and so far I've been having a lot of fun testing them out. Recently, I wanted to see if it's possible to use C++20 concepts to test the constraints of classes or functions. For example:
template <int N>
requires (N > 0)
class MyArray { ... };
template <int N>
concept my_array_compiles = requires {
typename MyArray<N>;
};
my_array_compiles<1>; // true
my_array_compiles<0>; // false
At first I didn't have any issues, but I encountered a case where static_assert in a dependent function prevents compilation, even though it appears in a requires expression. Here is an example that illustrates this:
template <bool b>
requires b
struct TestA {
void foo() {}
};
template <bool b>
struct TestB {
static_assert(b);
void foo() {}
};
template <template<bool> class T, bool b>
concept can_foo = requires (T<b> test) {
test.foo();
};
can_foo<TestA, true>; // true
can_foo<TestA, false>; // false
can_foo<TestB, true>; // true
// can_foo<TestB, false>; does not compile
TestA and TestB should work similarly for most use cases (although I found that TestB<false> can be used as a type as long as it isn't instantiated or dereferenced). However, my expectation was that a failed static_assert within a requires expression would cause it to evaluate to false instead. This is especially important for using library code that still uses static_assert. For example, std::tuple_element:
template <class T>
concept has_element_0 = requires {
typename tuple_element_t<0, T>;
};
has_element_0<tuple<int>>; // true
// has_element_0<tuple<>>; does not compile
When I pass in an empty tuple to the above concept, I get the error static_assert failed due to requirement '0UL < sizeof...(_Types)' "tuple_element index out of range". I've tested this on g++ 10.3.0 and clang 12.0.5. I was able to work around this issue by providing a wrapper that uses constraints, but it somewhat defeats the purpose since I am essentially preventing the compiler from seeing the static_assert by enforcing the same condition at a higher level.
template <size_t I, class T>
requires (I >= 0) && (I < tuple_size_v<T>)
using Type = tuple_element_t<I, T>;
template <class T>
concept has_element_0 = requires {
typename Type<0, T>;
};
has_element_0<tuple<int>>; // true
has_element_0<tuple<>>; // false
And it doesn't always work depending on how std::tuple_element is used:
template <size_t I, class T>
requires (I >= 0) && (I < tuple_size_v<T>)
tuple_element_t<I, T> myGet(const T& tup) {
return get<I>(tup);
}
template <class T>
concept has_element_0 = requires (T tup) {
myGet<0>(tup);
};
has_element_0<tuple<int>>; // true
// has_element_0<tuple<>>; does not compile
So ultimately my questions are: is this expected behavior that requires expressions don't take static_assert into account? If so, what was the reason for that design? And finally, is there a better way to accomplish my goal on classes with static_assert without using the above workaround?
Thanks for reading.
Yes, nothing in the content of the stuff you interact with is checked. Just the immediate context of the declaration.
In some cases with decltype the non immediate context of some constructs is checked, but any errors remain hard.
This was done (way back) to reduce the requirements on compilers. Only in what is known as "immediate context" do the compilers need to be able to cleanly back out when they see an error and continue compiling.
Static assert is never suitable for this purpose. Static assert, if hit, ends the compilation.
If you want to avoid the static assert (that is expected to end compilation) then you need to provide an alternative.
Once the concept is designed, create a variant for the not (!) of that concept:
#include <tuple>
#include <variant>
template <std::size_t I, class T>
requires (I >= 0) && (I < std::tuple_size_v<T>)
using Type = std::tuple_element_t<I, T>;
template <class T>
concept has_element_0 = requires {
typename Type<0, T>;
};
bool test1()
{
return has_element_0<std::tuple<int>>; // true
}
bool test2()
{
return has_element_0<std::tuple<>>; // false
}
template <std::size_t I, class T>
requires (I >= 0) && (I < std::tuple_size_v<T>)
std::tuple_element_t<I, T> myGet_impl(const T& tup) {
return get<I>(tup);
}
template <class T>
concept alt_has_element_0 = requires (T tup) {
myGet_impl<0>(tup);
};
template <class T>
auto myGet0();
template <class T>
requires (alt_has_element_0<T>)
auto myGet0(const T& tup)
{
return myGet_impl<0, T>(tup);
}
auto test3()
{
std::tuple<int> X{7};
return myGet0(X); // true
}
template <class T>
requires (!alt_has_element_0<T>)
auto myGet0(const T& tup)
{
return std::monostate{};
}
auto test4()
{
std::tuple<> X;
return myGet0(X); // true
}
see it here;
Notice for test4() to compile, the code above defiles what to do if we do not fulfill the requirements of the concept. I stole std::monostate from variant for this.
I'm working on upgrading some C++ code to take advantage of the new functionality in C++11. I have a trait class with a few functions returning fundamental types which would most of the time, but not always, return a constant expression. I would like to do different things based on whether the function is constexpr or not. I came up with the following approach:
template<typename Trait>
struct test
{
template<int Value = Trait::f()>
static std::true_type do_call(int){ return std::true_type(); }
static std::false_type do_call(...){ return std::false_type(); }
static bool call(){ return do_call(0); }
};
struct trait
{
static int f(){ return 15; }
};
struct ctrait
{
static constexpr int f(){ return 20; }
};
int main()
{
std::cout << "regular: " << test<trait>::call() << std::endl;
std::cout << "constexpr: " << test<ctrait>::call() << std::endl;
}
The extra int/... parameter is there so that if both functions are available after SFINAE, the first one gets chosen by overloading resolution.
Compiling and running this with Clang 3.2 shows:
regular: 0
constexpr: 1
So this appears to work, but I would like to know if the code is legal C++11. Specially since it's my understanding that the rules for SFINAE have changed.
NOTE: I opened a question here about whether OPs code is actually valid. My rewritten example below will work in any case.
but I would like to know if the code is legal C++11
It is, although the default template argument may be considered a bit unusual. I personally like the following style better, which is similar to how you (read: I) write a trait to check for a function's existence, just using a non-type template parameter and leaving out the decltype:
#include <type_traits>
namespace detail{
template<int> struct sfinae_true : std::true_type{};
template<class T>
sfinae_true<(T::f(), 0)> check(int);
template<class>
std::false_type check(...);
} // detail::
template<class T>
struct has_constexpr_f : decltype(detail::check<T>(0)){};
Live example.
Explanation time~
Your original code works† because a default template argument's point of instantiation is the point of instantiation of its function template, meaning, in your case, in main, so it can't be substituted earlier than that.
§14.6.4.1 [temp.point] p2
If a function template [...] is called in a way which uses the definition of a default argument of that function template [...], the point of instantiation of the default argument is the point of instantiation of the function template [...].
After that, it's just usual SFINAE rules.
† Atleast I think so, it's not entirely clear in the standard.
Prompted by #marshall-clow, I put together a somewhat more-generic version of an type-trait for detecting constexpr. I modelled it on std::invoke_result, but because constexpr depends on the inputs, the template arguments are for the values passed in, rather than the types.
It's somewhat limited, as the template args can only be a limited set of types, and they're all const when they get to the method call. You can easily test a constexpr wrapper method if you need other types, or non-const lvalues for a reference parameter.
So somewhat more of an exercise and demonstration than actually-useful code.
And the use of template<auto F, auto... Args> makes it C++17-only, needing gcc 7 or clang 4. MSVC 14.10.25017 can't compile it.
namespace constexpr_traits {
namespace detail {
// Call the provided method with the provided args.
// This gives us a non-type template parameter for void-returning F.
// This wouldn't be needed if "auto = F(Args...)" was a valid template
// parameter for void-returning F.
template<auto F, auto... Args>
constexpr void* constexpr_caller() {
F(Args...);
return nullptr;
}
// Takes a parameter with elipsis conversion, so will never be selected
// when another viable overload is present
template<auto F, auto... Args>
constexpr bool is_constexpr(...) { return false; }
// Fails substitution if constexpr_caller<F, Args...>() can't be
// called in constexpr context
template<auto F, auto... Args, auto = constexpr_caller<F, Args...>()>
constexpr bool is_constexpr(int) { return true; }
}
template<auto F, auto... Args>
struct invoke_constexpr : std::bool_constant<detail::is_constexpr<F, Args...>(0)> {};
}
Live demo with use-cases on wandbox
I'm working on upgrading some C++ code to take advantage of the new functionality in C++11. I have a trait class with a few functions returning fundamental types which would most of the time, but not always, return a constant expression. I would like to do different things based on whether the function is constexpr or not. I came up with the following approach:
template<typename Trait>
struct test
{
template<int Value = Trait::f()>
static std::true_type do_call(int){ return std::true_type(); }
static std::false_type do_call(...){ return std::false_type(); }
static bool call(){ return do_call(0); }
};
struct trait
{
static int f(){ return 15; }
};
struct ctrait
{
static constexpr int f(){ return 20; }
};
int main()
{
std::cout << "regular: " << test<trait>::call() << std::endl;
std::cout << "constexpr: " << test<ctrait>::call() << std::endl;
}
The extra int/... parameter is there so that if both functions are available after SFINAE, the first one gets chosen by overloading resolution.
Compiling and running this with Clang 3.2 shows:
regular: 0
constexpr: 1
So this appears to work, but I would like to know if the code is legal C++11. Specially since it's my understanding that the rules for SFINAE have changed.
NOTE: I opened a question here about whether OPs code is actually valid. My rewritten example below will work in any case.
but I would like to know if the code is legal C++11
It is, although the default template argument may be considered a bit unusual. I personally like the following style better, which is similar to how you (read: I) write a trait to check for a function's existence, just using a non-type template parameter and leaving out the decltype:
#include <type_traits>
namespace detail{
template<int> struct sfinae_true : std::true_type{};
template<class T>
sfinae_true<(T::f(), 0)> check(int);
template<class>
std::false_type check(...);
} // detail::
template<class T>
struct has_constexpr_f : decltype(detail::check<T>(0)){};
Live example.
Explanation time~
Your original code works† because a default template argument's point of instantiation is the point of instantiation of its function template, meaning, in your case, in main, so it can't be substituted earlier than that.
§14.6.4.1 [temp.point] p2
If a function template [...] is called in a way which uses the definition of a default argument of that function template [...], the point of instantiation of the default argument is the point of instantiation of the function template [...].
After that, it's just usual SFINAE rules.
† Atleast I think so, it's not entirely clear in the standard.
Prompted by #marshall-clow, I put together a somewhat more-generic version of an type-trait for detecting constexpr. I modelled it on std::invoke_result, but because constexpr depends on the inputs, the template arguments are for the values passed in, rather than the types.
It's somewhat limited, as the template args can only be a limited set of types, and they're all const when they get to the method call. You can easily test a constexpr wrapper method if you need other types, or non-const lvalues for a reference parameter.
So somewhat more of an exercise and demonstration than actually-useful code.
And the use of template<auto F, auto... Args> makes it C++17-only, needing gcc 7 or clang 4. MSVC 14.10.25017 can't compile it.
namespace constexpr_traits {
namespace detail {
// Call the provided method with the provided args.
// This gives us a non-type template parameter for void-returning F.
// This wouldn't be needed if "auto = F(Args...)" was a valid template
// parameter for void-returning F.
template<auto F, auto... Args>
constexpr void* constexpr_caller() {
F(Args...);
return nullptr;
}
// Takes a parameter with elipsis conversion, so will never be selected
// when another viable overload is present
template<auto F, auto... Args>
constexpr bool is_constexpr(...) { return false; }
// Fails substitution if constexpr_caller<F, Args...>() can't be
// called in constexpr context
template<auto F, auto... Args, auto = constexpr_caller<F, Args...>()>
constexpr bool is_constexpr(int) { return true; }
}
template<auto F, auto... Args>
struct invoke_constexpr : std::bool_constant<detail::is_constexpr<F, Args...>(0)> {};
}
Live demo with use-cases on wandbox
Scott Meyers posted content and status of his next book EC++11.
He wrote that one item in the book could be "Avoid std::enable_if in function signatures".
std::enable_if can be used as a function argument, as a return type or as a class template or function template parameter to conditionally remove functions or classes from overload resolution.
In this question all three solution are shown.
As function parameter:
template<typename T>
struct Check1
{
template<typename U = T>
U read(typename std::enable_if<
std::is_same<U, int>::value >::type* = 0) { return 42; }
template<typename U = T>
U read(typename std::enable_if<
std::is_same<U, double>::value >::type* = 0) { return 3.14; }
};
As template parameter:
template<typename T>
struct Check2
{
template<typename U = T, typename std::enable_if<
std::is_same<U, int>::value, int>::type = 0>
U read() { return 42; }
template<typename U = T, typename std::enable_if<
std::is_same<U, double>::value, int>::type = 0>
U read() { return 3.14; }
};
As return type:
template<typename T>
struct Check3
{
template<typename U = T>
typename std::enable_if<std::is_same<U, int>::value, U>::type read() {
return 42;
}
template<typename U = T>
typename std::enable_if<std::is_same<U, double>::value, U>::type read() {
return 3.14;
}
};
Which solution should be preferred and why should I avoid others?
In which cases "Avoid std::enable_if in function signatures" concerns usage as return type (which is not part of normal function signature but of template specializations)?
Are there any differences for member and non-member function templates?
Put the hack in the template parameters.
The enable_if on template parameter approach has at least two advantages over the others:
readability: the enable_if use and the return/argument types are not merged together into one messy chunk of typename disambiguators and nested type accesses; even though the clutter of the disambiguator and nested type can be mitigated with alias templates, that would still merge two unrelated things together. The enable_if use is related to the template parameters not to the return types. Having them in the template parameters means they are closer to what matters;
universal applicability: constructors don't have return types, and some operators cannot have extra arguments, so neither of the other two options can be applied everywhere. Putting enable_if in a template parameter works everywhere since you can only use SFINAE on templates anyway.
For me, the readability aspect is the big motivating factor in this choice.
std::enable_if relies on the "Substition Failure Is Not An Error" (aka SFINAE) principle during template argument deduction. This is a very fragile language feature and you need to be very careful to get it right.
if your condition inside the enable_if contains a nested template or type definition (hint: look for :: tokens), then the resolution of these nested tempatles or types are usually a non-deduced context. Any substitution failure on such a non-deduced context is an error.
the various conditions in multiple enable_if overloads cannot have any overlap because overload resolution would be ambiguous. This is something that you as an author need to check yourself, although you'd get good compiler warnings.
enable_if manipulates the set of viable functions during overload resolution which can have surprising interactions depending on the presence of other functions that are brought in from other scopes (e.g. through ADL). This makes it not very robust.
In short, when it works it works, but when it doesn't it can be very hard to debug. A very good alternative is to use tag dispatching, i.e. to delegate to an implementation function (usually in a detail namespace or in a helper class) that receives a dummy argument based on the same compile-time condition that you use in the enable_if.
template<typename T>
T fun(T arg)
{
return detail::fun(arg, typename some_template_trait<T>::type() );
}
namespace detail {
template<typename T>
fun(T arg, std::false_type /* dummy */) { }
template<typename T>
fun(T arg, std::true_type /* dummy */) {}
}
Tag dispatching does not manipulate the overload set, but helps you select exactly the function you want by providing the proper arguments through a compile-time expression (e.g. in a type trait). In my experience, this is much easier to debug and get right. If you are an aspiring library writer of sophisticated type traits, you might need enable_if somehow, but for most regular use of compile-time conditions it's not recommended.
Which solution should be preferred and why should I avoid others?
Option 1: enable_if in the template parameter
It is usable in Constructors.
It is usable in user-defined conversion operator.
It requires C++11 or later.
In my opinion, it is the more readable (pre-C++20).
It is easy to misuse and produce errors with overloads:
template<typename T, typename = std::enable_if_t<std::is_same<T, int>::value>>
void f() {/*...*/}
template<typename T, typename = std::enable_if_t<std::is_same<T, float>::value>>
void f() {/*...*/} // Redefinition: both are just template<typename, typename> f()
Notice the use of typename = std::enable_if_t<cond> instead of the correct std::enable_if_t<cond, int>::type = 0
Option 2 : enable_if in the return type
It cannot be used with constructors (which have no return type)
It cannot be used in user-defined conversion operator (as it is not deducible)
It can be used pre-C++11.
Second more readable IMO (pre-C++20).
Option 3: enable_if in a function parameter
It can be use pre-C++11.
It is usable in Constructors.
It cannot be used in user-defined conversion operators (they have no parameters)
It cannot be used in methods with a fixed number of arguments, such as unary/binary operators +, -, * and others.
It is safe for use in inheritance (see below).
Changes function signature (you have basically an extra as last argument void* = nullptr); this causes pointers pointers to the function to behave differently and so on.
Option4 (C++20) requires
There is now requires clauses
It is usable in Constructors
It is usable in user-defined conversion operator.
It requires C++20
IMO, the most readable
It is safe to use with inheritance (see below).
Can use directly template parameter of the class
template <typename T>
struct Check4
{
T read() requires(std::is_same<T, int>::value) { return 42; }
T read() requires(std::is_same<T, double>::value) { return 3.14; }
};
Are there any differences for member and non-member function templates?
There are subtle differences with inheritance and using:
According to the using-declarator (emphasis mine):
namespace.udecl
The set of declarations introduced by the using-declarator is found by performing qualified name lookup ([basic.lookup.qual], [class.member.lookup]) for the name in the using-declarator, excluding functions that are hidden as described below.
...
When a using-declarator brings declarations from a base class into a derived class, member functions and member function templates in the derived class override and/or hide member functions and member function templates with the same name, parameter-type-list, cv-qualification, and ref-qualifier (if any) in a base class (rather than conflicting). Such hidden or overridden declarations are excluded from the set of declarations introduced by the using-declarator.
So for both template argument and return type, methods are hidden is following scenario:
struct Base
{
template <std::size_t I, std::enable_if_t<I == 0>* = nullptr>
void f() {}
template <std::size_t I>
std::enable_if_t<I == 0> g() {}
};
struct S : Base
{
using Base::f; // Useless, f<0> is still hidden
using Base::g; // Useless, g<0> is still hidden
template <std::size_t I, std::enable_if_t<I == 1>* = nullptr>
void f() {}
template <std::size_t I>
std::enable_if_t<I == 1> g() {}
};
Demo (gcc wrongly finds the base function).
Whereas with argument, similar scenario works:
struct Base
{
template <std::size_t I>
void h(std::enable_if_t<I == 0>* = nullptr) {}
};
struct S : Base
{
using Base::h; // Base::h<0> is visible
template <std::size_t I>
void h(std::enable_if_t<I == 1>* = nullptr) {}
};
Demo
and with requires too:
struct Base
{
template <std::size_t I>
void f() requires(I == 0) { std::cout << "Base f 0\n";}
};
struct S : Base
{
using Base::f;
template <std::size_t I>
void f() requires(I == 1) {}
};
Demo
"Which solution should be preferred and why should I avoid others?"
When the question was asked, std::enable_if from <type_traits> was the best tool available, and the other answers are reasonable up to C++17.
Nowadays in C++20 we have direct compiler support via requires.
#include <concepts
template<typename T>
struct Check20
{
template<typename U = T>
U read() requires std::same_as <U, int>
{ return 42; }
template<typename U = T>
U read() requires std::same_as <U, double>
{ return 3.14; }
};
I want to create proxies for member functions and operators. They must have the same return type and parameters, and must be good for several classes, which are given as template parameters. Even if the class does not have the particular member function or operator, I want it to compile instead of failing with an error, essentially SFINAE. If X has a method f() and Y does not have any method named f, I need Proxy<X> to have an f() as well that calls X::f(), and I need Proxy<Y> to compile and instantiate without any problems.
Extracting the return type from a known function is no longer a problem, after a previous question of mine. However it fails with an error if there is no such function.
I already know several template metaprogramming tricks to determine whether a given function exists, and enable a certain feature if they do, however, they all work only on hardwired function names instead of arbitrary ones, which severely limits their use in this case since I need the same construct for several functions.
I only need to check whether any function with the given name exists, if there are overloaded variants I do not need to check if a specific one exists, automatic template deduction solves that (or so I hope)
My current code looks like this:
template <class T>
class Proxy
{
// using my resultof solution
template <class... Args>
resultof(T::f, Args...) f (Args... x)
{
return x.f(x...);
}
// using another return type extraction solution
template <class... Args>
typeof(T::f(std::declval<Args>()...)) f (Args... x)
{
return x.f(x...);
}
T x;
};
Which should compile even if T does not have any function named f. Unfortunately both version fail with an error.
The implementation of resultof being
#define resultof(f, ...) typeof(Param<__VA_ARGS__>::Func(f))
template <class... Args>
class Param
{
public:
template <class R>
static R Func (R (*) (Args...));
template <class R, class C>
static R Func (R (C::*) (Args...));
template <class R, class C>
static R Func (R (C::*) (Args...) const);
};
I suspect that
template<typename... Args>
decltype( std::declval<T&>().f(std::declval<Args>()...) )
f(Args&&... args)
{
return x.f(std::forward<Args>(args)...);
}
should trigger SFINAE and discard any instantiation of f for which the return type is ill-formed (e.g. ambiguous or non-existant overload) instead of a hard error. I'm not quite sure though because T is a parameter of proxy, not f and I simply can't parse the relevant parts of the Standard (around 14.8.2 I believe). None of the examples provided in the non normative notes seems to apply.
Failing that, it's possible to use
template<typename U = T&, typename... Args>
decltype( std::declval<U>().f(std::declval<Args>()...) )
f(Args&&... args)
{
return x.f(std::forward<Args>(args)...);
}
for which my compiler happily accepts proxy<int> p;, unlike with the first option. p.f(); results in a 'No matching function found' error, as is usual with SFINAE.
I recommend using the freestanding form of the operators where possible:
template<typename T, typename U>
auto operator+(Proxy<T> const& lhs, Proxy<U> const& rhs)
-> decltype( std::declval<T const&>() + std::declval<U const&>() )
{
return lhs.x + rhs.x;
}
is a possibility.
At first glance, this seems trivial:
template <typename T> class Proxy : public T { };
Nothing else in C++ will give Proxy<T> all the members of T, for any T. The only bit missing is the ctors, but from your question I infer that you already know how to forward those.
Background: Practically speaking, the set of possible member names of T is infinite. Therefore, you can't find .f() by name lookup in Proxy<T>, and the only other scope in which a member name is looked up is the base class scope.
You need to isolate the checking of the existence of f in the template parameter of proxy by an extra level. The following will allow you to call proxy<X>::f() in any way that you can call X::f():
template<typename T,typename ... Args>
struct f_result
{
typedef decltype(std::declval<T&>().f(std::declval<Args&&>()...)) type;
};
template<typename T>
struct proxy
{
T val;
template<typename ... Args>
typename f_result<T,Args...>::type
f(Args&& ... args)
{
return val.f(static_cast<Args&&>(args)...);
}
};
Quick test:
#include <iostream>
struct X
{
void f()
{
std::cout<<"X::f()"<<std::endl;
}
int f(int i)
{
std::cout<<"X::f("<<i<<")"<<std::endl;
return i;
}
};
struct Y
{};
struct Z
{
int f()
{
std::cout<<"Z::f()"<<std::endl;
return 42;
}
};
int main(int, char**)
{
proxy<X> px;
px.f();
int i=px.f(3);
std::cout<<"i="<<i<<std::endl;
proxy<Y> py;
proxy<Z> pz;
int j=pz.f();
std::cout<<"j="<<j<<std::endl;
}
This works OK with g++ 4.5 and g++ 4.6 in -std=c++0x mode.