There is a class C with methods foo, boo, boo_and_foo and foo_and_boo. Each method takes i,j,k and l clock cycles respectively, where i < j, k < i+j and l < i+j.
class C
{
public:
int foo() {...}
int boo() {...}
int boo_and_foo() {...}
int foo_and_boo() {...} };
In code one might write:
C c;
...
int i = c.foo() + c.boo();
But it would be better to have:
int i = c.foo_and_boo();
What changes or techniques could one make to the definition of C, that would allow similar syntax of the original usage, but instead have the compiler generate the latter.
Note that foo and boo are not commutative.
Idea 1 Thanks the comments!
I was thinking creater a cHelper class, and a cOps class with string member ops, so that after have cHelper ch;, we can write int i = ch.foo() + ch.boo();
ch.foo() does nothing but returns: cOps("foo")
ch.boo() also only returns: cOps("boo")
overload "+" operator for cOps class so that cOps("foo") + cOps("boo") actually returns cOps("foo,boo")
overload the int type conversion operator of cOps so that int i = cOps("foo") ; actually calls c.foo(); while int i = cOps("foo,boo") ; actually calls c.foo_and_boo();
A bit ugly ....
The objective sounds rather similar to what Todd Veldhuizen pioneered many moons ago for Blitz++: aggregating operations effectively into an expression tree to effectively change the order of computations. In the case of Blitz++ it was done for matrix operations. The technique is known as Expression Templates.
The overall idea would not be to aggregate a string and its operations, though, but rather to encode the necessary operations in the type returned. Similar to what is described in the question the evaluation of the expression is delayed until they are needed. By using specializations for specific operations the compiler would be made to choose the appropriate combations. It could look somewhat like this:
#include <iostream>
namespace C_ops { struct C_tag {}; }
struct C_foo;
struct C_bar;
class C: C_ops::C_tag {
int value;
public:
explicit C(int value): value(value) {}
C_foo foo() const;
C_bar bar() const;
int actual_foo() const {
std::cout << "compute foo(" << value << ")\n";
return value;
}
int actual_bar() const {
std::cout << "compute bar(" << value << ")\n";
return value;
}
int foo_and_bar(C const& r) const {
std::cout << "compute foo_and_bar(" << this->value << ", " << r.value << ")\n";
return this->value;
}
int bar_and_foo(C const& r) const {
std::cout << "compute bar_and_foo(" << this->value << ", " << r.value << ")\n";
return this->value;
}
};
struct C_foo: C_ops::C_tag {
C const& c;
C_foo(C const& c): c(c) {}
operator int() const { return c.actual_foo(); }
};
struct C_bar: C_ops::C_tag {
C const& c;
C_bar(C const& c): c(c) {}
operator int() const { return c.actual_bar(); }
};
C_foo C::foo() const { return C_foo(*this); }
C_bar C::bar() const { return C_bar(*this); }
template <typename L, typename R>
struct C_add;
template <>
struct C_add<C_foo, C_bar> {
C_foo l;
C_bar r;
C_add(C_foo const& l, C_bar const& r): l(l), r(r) {}
operator int() const { return l.c.foo_and_bar(r.c); }
};
template <>
struct C_add<C_bar, C_foo> {
C_bar l;
C_foo r;
C_add(C_bar const& l, C_foo const& r): l(l), r(r) {}
operator int() const { return l.c.bar_and_foo(r.c); }
};
// more specializations, e.g., to deal with C on the LHS
namespace C_ops {
template <typename L, typename R>
C_add<L, R> operator+(L const& l, R const& r) {
return C_add<L, R>(l, r);
}
}
template <typename... T> void use(T const&...) {}
int main()
{
C c0(0), c1(1);
int r0 = c0.foo();
int r1 = c0.bar();
int r2 = c0.foo() + c1.bar();
int r3 = c0.bar() + c1.foo();
std::cout << "done\n";
use(r0, r1, r2, r3); // make them used to not have them optimized away (for now)
}
With the setup above, the four expressions you mentioned would be covered. There are probably more specializations needed, e.g., to all c + c.foo(), c + c, etc. but the overall principle doesn't really change. You may want to protect actual_foo() and actual_bar() from being accessed and making the respective classes needing to call the friends.
The need for the extra namespace C_ops is merely to provide "catch-all" operators which hijack operator+() for all types, as long as they happen to be loooked up in this namespace. To do so, there is also a tag-type which has no other purpose than directing ADL (argument dependent look-up) to look into this namespace.
This is quite a cute technique which worked rather reliably with C++03: since the expressions are evaluated when the target type (in the above case int) is needed and C++03 didn't really allow for capturing the intermediate type while potentially involved temporary object were not around any more (the only was to catch the expression type in a deduced context would be when passing it to a function template) there were no life-time issues. With C++11 it is to capture the type of objects using auto which would capture the expression type, not evaluated result, there is probably some need to think properly about life-time of involved objects. The above example just used const objects but you'll probably need to be more careful and prevent certain temporary objects from being capture, primarily objects of type C. From your descriptions it isn't clear whether these would be expensive objects: the ideal solution would be to capture all objects by value but that may be too expensive.
Related
I am trying to create a class whose objects must contain a short description ("name") of what their value represent. Therefore the only public constructor should take a string as argument.
For the operations, however, I need to create temporary (no relevant name) object to calculate the value to be assigned to an already existing object. For that I have implemented a private constructor, which should not be used, neither directly nor indirectly, to instantiate a new object - these temporary objects should only be assigned to an already existing object, through operator=, which only copies the value rather than name and value.
The problem comes with the use of "auto". If a new variable is declared as follows:
auto newObj = obj + obj;
the compiler deduces the return type of operator+ and directly assign its result to newObj. This results in an object with a irrelevant name, which should not be possible to instantiate.
Also, deducing the type of an already existing object should still be possible from some functions, like:
auto newObj = obj.makeNewObjWithSameTypeButOtherName("Other name");
Follows a code demonstrating the problem:
#include <iostream>
#include <string>
using namespace std;
template<class T>
class Sample
{
public:
Sample(const string&);
Sample<T> makeNewObj(const string&);
// Invalid constructors
Sample();
Sample(const Sample&);
void operator=(const Sample&);
void operator=(const T&);
Sample<T> operator+(const Sample&) const;
void show(void);
private:
// Private constructor used during operations
Sample(const T&);
T _value;
string _name;
};
template<class T>
Sample<T>::Sample(const string& name)
{
this->_name = name;
this->_value = 0;
}
template<class T>
Sample<T>::Sample(const T&value)
{
this->_name = "Temporary variable";
this->_value = value;
}
template<class T>
Sample<T>
Sample<T>::makeNewObj(const string& name)
{
return Sample<T>(name);
}
template<class T>
void
Sample<T>::operator=(const Sample& si)
{
this->_name = this->_name; // Make explicit: Never change the name
this->_value = si._value;
}
template<class T>
void
Sample<T>::operator=(const T& value)
{
this->_name = this->_name; // Make explicit: Never change the name
this->_value = value;
}
template<class T>
Sample<T>
Sample<T>::operator+(const Sample& si) const
{
// if any of the two values are invalid, throw some error
return Sample<T>( this->_value + si._value );
}
template<class T>
void
Sample<T>::show(void)
{
cout << _name << " = " << _value << endl;
}
int main()
{
Sample<double> a("a"), b("b");
a = 1; // Sample::operator=(const T&)
b = 2.2; // Sample::operator=(const T&)
a.show(); // Output: a = 1
b.show(); // Output: b = 2.2
auto c = a.makeNewObj("c"); // Should be possible
c = a + b; // Sample::operator+(const Sample&) and Sample::operator=(const Sample&)
c.show(); // Output: c = 3.2
// Sample<double> d; // Compiler error as expected: undefined reference to `Sample::Sample()'
// auto f = a; // Compiler error as expected: undefined reference to `Sample::Sample(Sample const&)'
// This is what I want to avoid - should result in compiler error
auto g = a+c; // No compiler error: uses the private constructor Sample::Sample(const T&)
g.show(); // Output: Temporary variable = 4.2 <-- !! Object with irrelevant name
}
A quick workaround is to not return a temporary Sample<T> from operator +. Since you only want the value part you can just return that instead. That changes the code to
T operator+(const Sample&) const;
template<class T>
T
Sample<T>::operator+(const Sample& si) const
{
// if any of the two values are invalid, throw some error
return this->_value + si._value;
}
and then
auto g = a+c;
will make g whatever T is and g.show(); will not compile as g isn't a Sample<T>.
Sample<double> g = a+c;
Will also not work as it tries to construct g from a value and that constructor is private.
This will require adding
friend T operator+(T val, Sample<T> rhs) { return val + rhs._value; }
If you want to be able to chain additions like
a + a + a;
Somewhat related but also orthogonal to NathanOliver's answer:
You are mixing different concepts here. You have the notion of, essentially, NamedValue with Sample, but you are trying to make each expression formed out of arithmetics on NamedValue also a NamedValue. That is not going to work - the expression (by your semantics) does not have a name, so it should not be a NamedValue. Therefore, having NamedValue operator+(const NamedValue& other) is not meaningful.
Nathan's answer resolves this by making additions return T instead. That's pretty straightforward.
However, note that since a + b must have a type, you cannot stop auto g = a + b from compiling, even if it is demonstrably incorrect code. Ask Eigen, or any other expression template library. This remains true no matter how you choose the return type of operator+. So this wish of yours cannot be granted, unfortunately.
Still, I would suggest that you don't use plain T as return type but rather another class, say, Unnamed<T>:
template<class T>
class Unnamed
{
public:
explicit Unnamed(const T& value) : _value(value) {};
Unnamed<T> operator+(const Unnamed<T>& rhs) const
{
return Unnamed<T>(_value + rhs._value);
}
friend Unnamed operator+(const Unnamed& lhs, const Sample<T>& rhs);
friend Unnamed operator+(const Sample<T>& lhs, const Unnamed& rhs);
private:
T _value;
};
This allows you to do your checks and what have you on every operation (because the middle + in (a + b) + (c + d) cannot accept NamedValues, see above) instead of only when converting back to a named value.
Demo here.
You can increase the compile-time safety slightly by only allowing construction of Sample from Unnamed temporaries: https://godbolt.org/g/Lpz1m5
This could all be done more elegantly than sketched here. Note that this is moving exactly in the direction of expression templates though.
My suggestion would be to change the signature of the + operator (or any other operation needs implemented) to return a different type.
Than add an assignment operator accepting this "different type", but do not add a copy constructor - alternatively, for better error reporting, add a deleted one.
This would require more coding, since you would probably want to define "operations" on this type as well, so that chaining works.
If there are no another overloadings (say, f(T &) or f(volatile T &&)) of a (member) function template template< typename T > f(T &&);, then T && is so-called forwarding reference, and T is either U, or U & for some cv-qualified type U. But for cv-ref-qualifiers of member functions there is no such a rule. In struct S { void f() && { ; } }; a S::f() has always rvalue-reference qualifier.
In generic code it would be very useful to avoid a definition of 4 (or even 8, if we also consider volatile qualifier) overloadings of some member function, in cases if all of them doing generally the same thing.
Another problem that arises in this way, it is impossibility to define an effective cv-ref-qualifier of *this in a particular sense. Following code not allows one to determine whether the ref-qualifier of a member function operator () is && of &.
#include <type_traits>
#include <utility>
#include <iostream>
#include <cstdlib>
#define P \
{ \
using this_ref = decltype((*this)); \
using this_type = std::remove_reference_t< this_ref >; \
std::cout << qual() << ' ' \
<< (std::is_volatile< this_type >{} ? "volatile " : "") \
<< (std::is_const< this_type >{} ? "const " : "") \
<< (std::is_lvalue_reference< this_ref >{} ? "&" : "&&") \
<< std::endl; \
}
struct F
{
constexpr int qual() & { return 0; }
constexpr int qual() const & { return 1; }
constexpr int qual() && { return 2; }
constexpr int qual() const && { return 3; }
constexpr int qual() volatile & { return 4; }
constexpr int qual() volatile const & { return 5; }
constexpr int qual() volatile && { return 6; }
constexpr int qual() volatile const && { return 7; }
void operator () () & P
void operator () () const & P
void operator () () && P
void operator () () const && P
void operator () () volatile & P
void operator () () volatile const & P
void operator () () volatile && P
void operator () () volatile const && P
};
int
main()
{
{
F v;
F const c{};
v();
c();
std::move(v)();
std::move(c)();
}
{
volatile F v;
volatile F const c{};
v();
c();
std::move(v)();
std::move(c)();
}
return EXIT_SUCCESS;
}
But it would be very nice, if there was above syntax. I.e. decltype((*this)) denote exact cv-ref-qualified type of *this. It would not be a breaking-change to introduce such a syntax into coming version of the C++ standard at my mind. But && as forwarding cv-ref-qualifier is (and it looks like an omission of the committee (namely, core language working group)).
Another sequence is possible to denote both the member function cv-ref-qualifier and cv-ref-qualified type of *this into its body: auto &&, decltype(&&) etc.
Is there a proposal regarding this issue, prepared for use in C++17?
Yes, there are such proposals.
Background:
Since we already have forwarding references in template functions, you could simply turn your member function into a template friend function (and protect it via enable_if from being used with any other class than F, if required).
Now, maybe you want your really, really want to use your function as a member function, because you really, really like that syntax so much better.
The proposals:
Look up unified call syntax proposals, for instance: n4174
If something like that is accepted, it you will be able to use free functions like member functions of the first argument. This would cover the example code you linked in your first comment. Admittedly, it would not cover operator(), but I consider that a minor nuisance compared to writing 8 overloads :-)
Imagine you have a number of overloaded methods that (before C++11) looked like this:
class MyClass {
public:
void f(const MyBigType& a, int id);
void f(const MyBigType& a, string name);
void f(const MyBigType& a, int b, int c, int d);
// ...
};
This function makes a copy of a (MyBigType), so I want to add an optimization by providing a version of f that moves a instead of copying it.
My problem is that now the number of f overloads will duplicate:
class MyClass {
public:
void f(const MyBigType& a, int id);
void f(const MyBigType& a, string name);
void f(const MyBigType& a, int b, int c, int d);
// ...
void f(MyBigType&& a, int id);
void f(MyBigType&& a, string name);
void f(MyBigType&& a, int b, int c, int d);
// ...
};
If I had more parameters that could be moved, it would be unpractical to provide all the overloads.
Has anyone dealt with this issue? Is there a good solution/pattern to solve this problem?
Thanks!
Herb Sutter talks about something similar in a cppcon talk
This can be done but probably shouldn't. You can get the effect out using universal references and templates, but you want to constrain the type to MyBigType and things that are implicitly convertible to MyBigType. With some tmp tricks, you can do this:
class MyClass {
public:
template <typename T>
typename std::enable_if<std::is_convertible<T, MyBigType>::value, void>::type
f(T&& a, int id);
};
The only template parameter will match against the actual type of the parameter, the enable_if return type disallows incompatible types. I'll take it apart piece by piece
std::is_convertible<T, MyBigType>::value
This compile time expression will evaluate to true if T can be converted implicitly to a MyBigType. For example, if MyBigType were a std::string and T were a char* the expression would be true, but if T were an int it would be false.
typename std::enable_if<..., void>::type // where the ... is the above
this expression will result in void in the case that the is_convertible expression is true. When it's false, the expression will be malformed, so the template will be thrown out.
Inside the body of the function you'll need to use perfect forwarding, if you are planning on copy assigning or move assigning, the body would be something like
{
this->a_ = std::forward<T>(a);
}
Here's a coliru live example with a using MyBigType = std::string. As Herb says, this function can't be virtual and must be implemented in the header. The error messages you get from calling with a wrong type will be pretty rough compared to the non-templated overloads.
Thanks to Barry's comment for this suggestion, to reduce repetition, it's probably a good idea to create a template alias for the SFINAE mechanism. If you declare in your class
template <typename T>
using EnableIfIsMyBigType = typename std::enable_if<std::is_convertible<T, MyBigType>::value, void>::type;
then you could reduce the declarations to
template <typename T>
EnableIfIsMyBigType<T>
f(T&& a, int id);
However, this assumes all of your overloads have a void return type. If the return type differs you could use a two-argument alias instead
template <typename T, typename R>
using EnableIfIsMyBigType = typename std::enable_if<std::is_convertible<T, MyBigType>::value,R>::type;
Then declare with the return type specified
template <typename T>
EnableIfIsMyBigType<T, void> // void is the return type
f(T&& a, int id);
The slightly slower option is to take the argument by value. If you do
class MyClass {
public:
void f(MyBigType a, int id) {
this->a_ = std::move(a); // move assignment
}
};
In the case where f is passed an lvalue, it will copy construct a from its argument, then move assign it into this->a_. In the case that f is passed an rvalue, it will move construct a from the argument and then move assign. A live example of this behavior is here. Note that I use -fno-elide-constructors, without that flag, the rvalue cases elides the move construction and only the move assignment takes place.
If the object is expensive to move (std::array for example) this approach will be noticeably slower than the super-optimized first version. Also, consider watching this part of Herb's talk that Chris Drew links to in the comments to understand when it could be slower than using references. If you have a copy of Effective Modern C++ by Scott Meyers, he discusses the ups and downs in item 41.
You may do something like the following.
class MyClass {
public:
void f(MyBigType a, int id) { this->a = std::move(a); /*...*/ }
void f(MyBigType a, string name);
void f(MyBigType a, int b, int c, int d);
// ...
};
You just have an extra move (which may be optimized).
My first thought is that you should change the parameters to pass by value. This covers the existing need to copy, except the copy happens at the call point rather than explicitly in the function. It also allows the parameters to be created by move construction in a move-able context (either unnamed temporaries or by using std::move).
Why you would do that
These extra overloads only make sense, if modifying the function paramers in the implementation of the function really gives you a signigicant performance gain (or some kind of guarantee). This is hardly ever the case except for the case of constructors or assignment operators. Therefore, I would advise you to rethink, whether putting these overloads there is really necessary.
If the implementations are almost identical...
From my experience this modification is simply passing the parameter to another function wrapped in std::move() and the rest of the function is identical to the const & version. In that case you might turn your function into a template of this kind:
template <typename T> void f(T && a, int id);
Then in the function implementation you just replace the std::move(a) operation with std::forward<T>(a) and it should work. You can constrain the parameter type T with std::enable_if, if you like.
In the const ref case: Don't create a temporary, just to to modify it
If in the case of constant references you create a copy of your parameter and then continue the same way the move version works, then you may as well just pass the parameter by value and use the same implementation you used for the move version.
void f( MyBigData a, int id );
This will usually give you the same performance in both cases and you only need one overload and implementation. Lots of plusses!
Significantly different implementations
In case the two implementations differ significantly, there is no generic solution as far as I know. And I believe there can be none. This is also the only case, where doing this really makes sense, if profiling the performance shows you adequate improvements.
You might introduce a mutable object:
#include <memory>
#include <type_traits>
// Mutable
// =======
template <typename T>
class Mutable
{
public:
Mutable(const T& value) : m_ptr(new(m_storage) T(value)) {}
Mutable(T& value) : m_ptr(&value) {}
Mutable(T&& value) : m_ptr(new(m_storage) T(std::move(value))) {}
~Mutable() {
auto storage = reinterpret_cast<T*>(m_storage);
if(m_ptr == storage)
m_ptr->~T();
}
Mutable(const Mutable&) = delete;
Mutable& operator = (const Mutable&) = delete;
const T* operator -> () const { return m_ptr; }
T* operator -> () { return m_ptr; }
const T& operator * () const { return *m_ptr; }
T& operator * () { return *m_ptr; }
private:
T* m_ptr;
char m_storage[sizeof(T)];
};
// Usage
// =====
#include <iostream>
struct X
{
int value = 0;
X() { std::cout << "default\n"; }
X(const X&) { std::cout << "copy\n"; }
X(X&&) { std::cout << "move\n"; }
X& operator = (const X&) { std::cout << "assign copy\n"; return *this; }
X& operator = (X&&) { std::cout << "assign move\n"; return *this; }
~X() { std::cout << "destruct " << value << "\n"; }
};
X make_x() { return X(); }
void fn(Mutable<X>&& x) {
x->value = 1;
}
int main()
{
const X x0;
std::cout << "0:\n";
fn(x0);
std::cout << "1:\n";
X x1;
fn(x1);
std::cout << "2:\n";
fn(make_x());
std::cout << "End\n";
}
This is the critical part of the question:
This function makes a copy of a (MyBigType),
Unfortunately, it is a little ambiguous. We would like to know what is the ultimate target of the data in the parameter. Is it:
1) to be assigned to an object that existing before f was called?
2) or instead, stored in a local variable:
i.e:
void f(??? a, int id) {
this->x = ??? a ???;
...
}
or
void f(??? a, int id) {
MyBigType a_copy = ??? a ???;
...
}
Sometimes, the first version (the assignment) can be done without any copies or moves. If this->x is already long string, and if a is short, then it can efficiently reuse the existing capacity. No copy-construction, and no moves. In short, sometimes assignment can be faster because we can skip the copy contruction.
Anyway, here goes:
template<typename T>
void f(T&& a, int id) {
this->x = std::forward<T>(a); // is assigning
MyBigType local = std::forward<T>(a); // if move/copy constructing
}
If the move version will provide any optimization then the implementation of the move overloaded function and the copy one must be really different. I don't see a way to get around this without providing implementations for both.
I wish to create many functions with the same parameters, for example:
const int add(const int a, const int b) {
return (a + b);
}
decltype(add) subtract {
return (a - b);
}
/* many more functions */
The purpose being that I am able to easily change the types of the parameters once to change all of the functions. I know that this is possible with macros as so:
#define INT_OPERATION(name) const int name (const int a, const int b)
INT_OPERATION(add) { return (a + b); }
INT_OPERATION(subtract) {return (a - b); }
However, I dislike the use of macros. Is there a safer way of doing this?
A function signature cannot be typedefed. Only a function's type. So it's valid to say :
typedef int INT_OPERATION(int a, int b);
and then forward declare a function having this type :
INT_OPERATION Add;
but when it comes to defining the function you'd have to specify arguments, so the follwing is invalid
INT_OPERATION Add { /* No can do */ }
(as soon as you place a set of () after Add you'll be declaring a function returning a plain function which is not valid C++)
On generic types
The same procedure has similar limitations when using the tools for generic programming. You can declare the following typedef :
template<typename T>
using INT_OPERATION = const T(T const, T const);
And then use it for a function (forward) declaration
INT_OPERATION<int> add;
int main() {
std::cout << add(1, 2) << std::endl;
return 0;
}
but when it comes to defining it, you'd have to be mandane
const int add(int const a, int const b) {
return a + b;
}
Demo
Maybe something like that:
#include <iostream>
#include <functional>
typedef std::function<int(int, int)> IntFunction;
IntFunction add = [](int a, int b) {return a + b;};
IntFunction substract = [](int a, int b) {return a - b;};
int main(int, char**) {
std::cout << add(1, 2) << std::endl;
std::cout << substract(1, 2) << std::endl;
return 0;
}
Still boilerplate code, but worth a try ;)
If these were member functions you could declare their common type (typedef const int Fn(int, int)), declare the member functions inside the class using that type:
Fn add, subtract;
Then you define them outside the class in the normal way. Doesn't save boilerplate, but at least means it won't compile if you make a typo in the definitions.
Alternatively for non member functions see the question I linked to which has some clever ways to define function pointers rather than functions.
If your primary concern is being able to "easily change the types of the parameters", it's straightforward to write them as templates so that you never have to change the types of the parameters:
template <typename T1, typename T2>
auto add (const T1 &a, const T2 &b) -> decltype(a + b) {
return (a + b);
}
This also handles cases where a and b are not of the same type, or where a + b is not the same type as a or b.
In C++03, Boost's Foreach, using this interesting technique, can detect at run-time whether an expression is an lvalue or an rvalue. (I found that via this StackOverflow question: Rvalues in C++03 )
Here's a demo of this working at run-time
(This is a more basic question that arose while I was thinking about this other recent question of mine. An answer to this might help us answer that other question.)
Now that I've spelled out the question, testing rvalue-ness in C++03 at compile-time, I'll talk a little about the things I've been trying so far.
I want to be able to do this check at compile-time. It's easy in C++11, but I'm curious about C++03.
I'm trying to build upon their idea, but would be open to different approaches also. The basic idea of their technique is to put this code into a macro:
true ? rvalue_probe() : EXPRESSION;
It is 'true' on the left of the ?, and therefore we can be sure that EXPRESSION will never be evaluated. But the interesting thing is that the ?: operator behaves differently depending on whether its parameters are lvalues or rvalues (click that link above for details). In particular, it will convert our rvalue_probe object in one of two ways, depending on whether EXPRESSION is an lvalue or not:
struct rvalue_probe
{
template< class R > operator R () { throw "rvalue"; }
template< class L > operator L & () const { throw "lvalue"; }
template< class L > operator const L & () const { throw "const lvalue"; }
};
That works at runtime because the thrown text can be caught and used to analyze whether the EXPRESSION was an lvalue or an rvalue. But I want some way to identify, at compile-time, which conversion is being used.
Now, this is potentially useful because it means that, instead of asking
Is EXPRESSION an rvalue?
we can ask:
When the compiler is compiling true ? rvalue_probe() : EXPRESSION, which of the two overloaded operators, operator X or operator X&, is selected?
( Ordinarily, you could detect which method was called by changing the return types and getting the sizeof it. But we can't do that with these conversion operators, especially when they're buried inside the ?:. )
I thought I might be able to use something like
is_reference< typeof (true ? rvalue_probe() : EXPRESSION) > :: type
If the EXPRESSION is an lvalue, then the operator& is selected and I hoped that the whole expression would then be a & type. But it doesn't seem to work. ref types and non-ref types are pretty hard (impossible?) to distinguish, especially now that I'm trying to dig inside a ?: expression to see which conversion was selected.
Here's the demo code pasted here:
#include <iostream>
using namespace std;
struct X {
X(){}
};
X x;
X & xr = x;
const X xc;
X foo() { return x; }
const X fooc() { return x; }
X & foor() { return x; }
const X & foorc() { return x; }
struct rvalue_probe
{
template< class R > operator R () { throw "rvalue"; }
// template< class R > operator R const () { throw "const rvalue"; } // doesn't work, don't know why
template< class L > operator L & () const { throw "lvalue"; }
template< class L > operator const L & () const { throw "const lvalue"; }
};
typedef int lvalue_flag[1];
typedef int rvalue_flag[2];
template <typename T> struct isref { static const int value = 0; typedef lvalue_flag type; };
template <typename T> struct isref<T&> { static const int value = 1; typedef rvalue_flag type; };
int main() {
try{ true ? rvalue_probe() : x; } catch (const char * result) { cout << result << endl; } // Y lvalue
try{ true ? rvalue_probe() : xc; } catch (const char * result) { cout << result << endl; } // Y const lvalue
try{ true ? rvalue_probe() : xr; } catch (const char * result) { cout << result << endl; } // Y lvalue
try{ true ? rvalue_probe() : foo(); } catch (const char * result) { cout << result << endl; } // Y rvalue
try{ true ? rvalue_probe() : fooc(); } catch (const char * result) { cout << result << endl; } // Y rvalue
try{ true ? rvalue_probe() : foor(); } catch (const char * result) { cout << result << endl; } // Y lvalue
try{ true ? rvalue_probe() : foorc(); } catch (const char * result) { cout << result << endl; } // Y const lvalue
}
(I had some other code here at the end, but it's just confusing things. You don't really want to see my failed attempts at an answer! The above code demonstrates how it can test lvalue-versus-rvalue at runtime.)
It took some effort, but here's a tested and working is_lvalue macro that correctly handles const struct S function return types. It relies on const struct S rvalues not binding to const volatile struct S&, while const struct S lvalues do.
#include <cassert>
template <typename T>
struct nondeducible
{
typedef T type;
};
char (& is_lvalue_helper(...))[1];
template <typename T>
char (& is_lvalue_helper(T&, typename nondeducible<const volatile T&>::type))[2];
#define is_lvalue(x) (sizeof(is_lvalue_helper((x),(x))) == 2)
struct S
{
int i;
};
template <typename T>
void test_()
{
T a = {0};
T& b = a;
T (* c)() = 0;
T& (* d)() = 0;
assert (is_lvalue(a));
assert (is_lvalue(b));
assert (!is_lvalue(c()));
assert (is_lvalue(d()));
}
template <typename T>
void test()
{
test_<T>();
test_<const T>();
test_<volatile T>();
test_<const volatile T>();
}
int main()
{
test<int>();
test<S>();
}
Edit: unnecessary extra parameter removed, thanks Xeo.
Edit again: As per the comments, this works with GCC but relies on unspecified behaviour in C++03 (it's valid C++11) and fails some other compilers. Extra parameter restored, which makes it work in more cases. const class rvalues give a hard error on some compilers, and give the correct result (false) on others.
The address-of operator (&) can only be used with an lvalue. So if you used it in an SFINAE test, you could distinguish at compile-time.
A static assertion could look like:
#define STATIC_ASSERT_IS_LVALUE(x) ( (sizeof &(x)), (x) )
A trait version might be:
template<typename T>
struct has_lvalue_subscript
{
typedef char yes[1];
typedef char no[2];
yes fn( char (*)[sizeof (&(((T*)0)->operator[](0))] );
no fn(...);
enum { value = sizeof(fn(0)) == 1 };
};
and could be used like
has_lvalue_subscript< std::vector<int> >::value
(Warning: not tested)
I can't think of any way to test an arbitrary expression valid in the caller's context, without breaking compilation on failure.