在C++11标准中,auto关键字被引入以简化类型声明,它允许编译器自动推断变量的类型。然而,当auto与表达式模板(expression templates)和代理对象(proxies)一起使用时,可能会出现问题。简单地用auto替换类型可能会导致未定义行为。下面是一个示例代码,展示了这个问题:
#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;
}
那么,如何解决这个问题呢?有关于添加一个operator auto的讨论,可以在类中定义它。然而,可能需要一段时间才能得到类似的东西。Herb Sutter在他的"Almost Always Auto"中说这是一个特性而不是一个bug,"因为有一个方便的方式来拼写'捕获列表或代理'和'解析计算',这取决于意思"。
以下是一些讨论这个问题的代码:
auto a = matrix{...}, b = matrix{...};
// some type that does lazy eval
auto ab = a * b;
// to capture the lazy-eval proxy
auto c = matrix{ a * b };
// to force computation
这不仅可能危险,而且可能会很繁琐。如果matrix接受一些模板参数,比如维度和类型,现在有:
auto a = matrix<2, 3, double>{...}, b = matrix<3, 2, double>{...};
// some type that does lazy eval
auto ab = a * b;
// to capture the lazy-eval proxy
auto c = matrix<3, 3, double>{ a * b };
// to force computation
在这种情况下,正在迅速失去auto的好处。是否有一些方法可以让拥有auto和表达式模板呢?这里有一个解决方案,虽然不是完美的,但认为这是在不改变语言的情况下能做的最好的事情。
将模拟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;
}
这只是一个创建一个_auto变量的操作,当分配给它时,它返回分配的任何东西转换为另一种类型,而在默认情况下是相同的类型。然后像这样专门化operator_auto_type:
// For my::string for Motti's example
namespace operator_auto {
template <class T>
struct operator_auto_type<my::string::concat<T>> {
using type = my::string;
};
}
// For vector bool
namespace operator_auto {
template <>
struct operator_auto_type<std::vector<bool>::reference> {
using type = bool;
};
}
现在要使用它,每当使用auto与可能产生代理的表达式时,只需要包括一个额外的分配给_auto。以下是如何使用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;
注意对于a1,实际上是分配给一个my::string。在这种情况下,分配给_auto将变成一个无操作。
完整的源代码可以在这里查看:。对于可运行的版本,可以查看:。
至于名字_auto,选择它是因为它很短,下划线有点暗示"flatten"或"collapse",导致一个记忆的"collapse auto",这有点暗示了想要的。然而,如果愿意,可以很容易地改变它。