Back in 2011, Motti Lanzkron wrote an article titled "Inferring Too Much".
The problem brought to light by the article is that C++11 auto
interacts badly with expression templates and proxies. Just replacing the type with auto can cause undefined behavior as shown by the following lines of code taken from the article above.
#include <vector>
#include <iostream>
#include <limits>
std::vector<bool> to_bits(unsigned int n) {
const int bits = std::numeric_limits<unsigned int>::digits;
std::vector<bool> ret(bits);
for(int i = 0, mask = 1; i < bits; ++i, mask *= 2)
ret[i] = (n & mask) != 0;
return ret;
}
int main()
{
bool b = to_bits(42)[3];
auto a = to_bits(42)[3];
std::cout << std::boolalpha << b << std::endl;
std::cout << std::boolalpha << a << std::endl;
}
So how do we fix it?
There has been some talk about adding an operator auto that you could define in your class. However, it might be some time before we get something like that.
Herb Sutter in his "Almost Always Auto" says this is a feature and not a bug, "because you have a convenient way to spell both 'capture the list or proxy' and 'resolve the computation' depending which you mean".
Here is some code discussing this:
auto a = matrix{...}, b = matrix{...}; auto ab = a * b; auto c = matrix{ a * b };
Unfortunately, not only is this potentially dangerous, but it can be tedious. What if matrix takes some template parameters such as dimensions and type. Now you have:
auto a = matrix<2,3,double>{...}, b = matrix<3,2,double>{...}; auto ab = a * b; auto c = matrix<3,3,double>{ a * b };
In this scenario, we are fast losing the benefits of auto. Is there some way that we can have our auto and our expression templates. Here is a workaround, which admittedly is not perfect, but I think it is the best we can do without changing the language.
We are going to simulate operator auto:
namespace operator_auto {
template <class T> struct operator_auto_type {
using type = T;
};
struct operator_auto_imp {
template <class T> typename operator_auto_type<T>::type operator=(T &&t){
return std::forward<T>(t);
}
};
namespace {
operator_auto_imp _auto;
}
}
All this does is create a variable _auto
that when assigned to it returns whatever was assigned converted to another type which in the default case is the same type.
Then we specialize operator_auto_type
like this:
namespace operator_auto {
template <class T>
struct operator_auto_type<my::string::concat<T> >
{
using type = my::string;
};
}
namespace operator_auto {
template <>
struct operator_auto_type<std::vector<bool>::reference>
{
using type = bool;
};
}
Now to use it, whenever we use auto with an expression that might yield a proxy, we just include an addition assignment to _auto
. Here is how we would use it with my::string
:
using operator_auto::_auto;
my::string a("hello"), b(" "), c("world"), d("!");
auto s = _auto = a + b + c + d;
auto a1 = _auto = a;
std::cout << s << std::endl;
Notice that for a1
, we are actually assigning to a my::string
. In this case, the assignment to _auto
will become a no-op.
For full source code for this, take a look at https://gist.github.com/jbandela/8042689. For a runnable version, look at http://ideone.com/eLyg7T.
As for the name _auto
, I chose it because it was short and the underscore kind of suggested "flatten" or "collapse" leading to a mnemonic of "collapse auto" which is kind of suggestive of what you want. However, you can easily change it if you wish.
Let me know what you think in the comments. I welcome your comments, suggestions, and ideas.