Jul 15, 2014 11:23
Появился тут кодец, который, если упростить его до безобразия, выглядит так:
struct X
{
};
class A
{
public:
A(const X* x) {}
};
class B : public A
{
public:
B() : A(&X()) {}
};
Ну то есть есть класс A, который конструктором принимает некоторую структуру для целей собственной инициализации (параметры инициализации там лежат, чтобы не рисовать большой-пребольшой конструктор), а есть класс B, который хочет проинициализировать свою базу A такой структурой, а при этом своих клиентов он грузить подробностями не хочет. Соответственно классу B нужно успеть создать такую структуру в узкий промежуток между вызовом B::B и передачей управления A::A, а узкий промежуток заключается только в вычислении параметров A::A, соответственно придумать что-то умнее, как создать темпоралку X и передать ее в A, сложно.
Проблема же в том, что A принимает не константную ссылку на X, а константный указатель, и если визуальнику на это пофиг, то остальным компилятором нет, и они справедливо указывают на пункт стандарта, который утверждает, что операндом & должно быть lvalue (а темпоралка им не является). Вопрос -- как быть? Если можно поменять A, то задачки нет, а предположим A поменять нельзя. Какие есть варианты?
1) Если бы можно было бы где-то написать хотя бы стейтмент, то можно было бы написать так
const X& x = X();
A(&x)
но это не наш кейс
2) Если можно было бы изменить X, то можно было бы приписать ей функцию
const X* ptr() const { return this; }
и, соответственно, вызов заменить на
B(): A(X().ptr()) {}
3) Можно не меняя X пронаследовать его, а в наследнике сделать такую функцию
struct Y : X
{
const X* ptr() const { return this; }
};
и, соответственно, вызов заменить на
B() : A(Y().ptr()) {}
4) Можно написать шаблончик, который возьмет const X&, и вернет указатель
template const T* ptr(const T& t) { return &t; }
и, соответственно, вызов заменить на
B() : A(ptr(X())) {}
5) И, наконец, мой фаворит. Написать лямбду, которая делает то же самое
B() : A([](const X& x){ return &x; }(X())) {}
И все.
prog