2015-10-05 3 views
2
#include "MassivePOD.h" 
#include "DependantClass.h" // Constructor: `DependantClass(MassivePOD&)` 

class ExplicitSolution 
{ 
public: 
    ExplicitSolution() : m_pod{...}, m_dep{m_pod} { }; 
private: 
    MassivePOD m_pod; 
    DependantClass m_dep; 
}; 

template<typename... T> 
class TemplatedSolution 
{ 
public: 
    template<typename... TupArgs> TemplatedSolution(TupArgs...); 
private: 
    // Can assume in-order construction works 
    std::tuple<T...> m_tuple; 
}; 


int main() 
{ 
    ExplicitSolution a{}; // Easy! 
    TemplatedSolution<MassivePOD, DependantClass> b{ 
     std::forward_as_tuple(...) // Forwarded to MassivePOD constructor 
     , std::forward_as_tuple(???) // Forwarded to DependantClass constructor 
     }; // Hard? 
} 

Надеюсь, этот пример иллюстрирует мою проблему. Я хотел бы сделать ссылку на ранее построенный член std::tuple до того, как будет построено целое std::tuple. Есть ли элегантное решение? Я знаю, что возможно с использованием void * хакеров, но я бы скорее попросил помощи, прежде чем спуститься по этой темной одинокой дороге.Использовать ссылки на std :: tuple во время строительства?

Идея 1

Я попытался сделать get_ref функцию, но я получаю ту же проблему, что я не могу получить доступ к функции-члена объекта, который не был создан еще. Впрочем, вот одна из моих попыток.

#include <tuple> 
#include <utility> 

class Foo // simplified base-case 
{ 
public: 
    constexpr Foo(int x, char y, int& z) : m_tup{x, y, z*5.0} { }; 

    constexpr int& get_int() { return std::get<0>(m_tup); }; 
    constexpr char& get_char() { return std::get<1>(m_tup); }; 
    constexpr double& get_double() { return std::get<2>(m_tup); }; 

private: 
    std::tuple<int, char, double> m_tup; 
}; 

int main() 
{ 
    auto super = Foo(5, 'a', ::get_int()); // ??? 
} 

Demo

Идея 2

Тогда я подумал, может быть, я мог бы сделать что-то вроде того, что std::function делает с std::place_holders, поэтому статический объект, который содержит указатель/ссылку, где каждый элемент std::tuple было бы. Я думаю, что это стоит попробовать, но я не знаю, как его реализовать на практике ...

+0

Почему вы делаете 'forward_as_tuple'? Использует ли 'ExpensiveToCopyPOD' кортеж в конструкторе? – Claudiu

+0

@Claudiu Я расширяю значения кортежа в конструкторе 'ExpensiveToPOD', и я делаю это, потому что вывод конструктивной арности - действительно сложная проблема сама по себе. –

+0

Обратите внимание, что элементы 'std :: tuple' не гарантируются для создания/инициализации в порядке аргументов шаблона. Фактически, по крайней мере одна популярная реализация делает это «назад». – aschepler

ответ

1

Другое предложение: класс кортежа, вместо того, чтобы принимать параметры для конструкторов членов, вместо этого принимает функции, которые получают экземпляр, построенный на этапе, и который возвращает члены. Эти функции затем могут ссылаться на ранее построенные члены через геттеры. Все, что вам нужно сделать, это убедиться, что геттеры для более ранних членов работают и не вызывают неопределенное поведение, если вызываются, когда строятся более поздние члены.

Вот пример жесткого кодирования для двух элементов, демонстрирующих принцип. Я оставлю это вам, чтобы заставить его работать с элементами n. Обратите внимание, что по существу вы бы перепрограммировали кортеж здесь - я не уверен, можете ли вы сделать это с существующим std::tuple.

template<typename A, typename B> 
class TemplatedSolution 
{ 
public: 
    template<typename AF, typename BF> TemplatedSolution(AF af, BF bf) 
     : a(af(*this)) 
     , b(bf(*this)) 
    { 
    } 

    A& get_first() { 
     return a; 
    } 

    B& get_second() { 
     return b; 
    } 

private: 
    A a; 
    B b; 
}; 

Использование:

typedef TemplatedSolution<MassivePOD, DependantClass> ClassT; 

ClassT b{ 
    [](ClassT& res) { return MassivePOD(); }, 
    [](ClassT& res) { return DependantClass(res.get_first()); }, 
}; 

Из-за оптимизации возврата стоимости, то MassivePOD s получит построенный прямо в экземпляре TemplatedSolution.

Доступен полный рабочий пример, который не показывает копии. here.

+1

Это замечательная идея! :) Спасибо, я думаю, я пойду с этим решением! –

+0

Хмм на самом деле получается, что для вариативных шаблонов мне пришлось передать 'std :: forward_as_tuple' вместо полностью построенного объекта, потому что мне пришлось построить через' std :: allocator .construct', который недостаточно умен использовать RVO. Вот решение, которое я нашел для работы! (Должен был поместить некоторый из грязного кода TMP для решения, чтобы работать, поэтому вы можете сбрасывать пространства имен WLOG!) [Demo] (http://coliru.stacked-crooked.com/a/fef28978cfdc9f0e) –

0

Как насчет использования std::shared_ptr и хранения его как std::shared_ptr в TupleWrapper вместо этого?

auto p_first_member_tmp = std::make_shared<MassivePOD>(
    std::forward_as_tuple(...)); 

TupleWrapper<std::shared_ptr<MassivePOD>, DependantClass> wrapper{ 
    p_first_member_tmp, 
    std::forward_as_tuple(*p_first_member_tmp, ...)} 

Это дополнительный уровень косвенности, но ваш дорогой к копии или-ход структуры не будут скопированы или перемещены по крайней мере.

Я рассматривал, почему это никогда не проблема на языке, таком как Python, и это потому, что объект создается только один раз, а затем все остальное просто указывает на него - это приближается к этому подходу здесь. Вам также не нужно будет отслеживать время жизни объекта, так как shared_ptr делает это уже для вас.

+0

Хм, это шаг в правильном направлении, но мне все же хотелось бы, чтобы он был внутри кортежа. Одна из причин, по которой я хочу, чтобы POD был в кортеже, - это то, что я могу получить баффы локалей; вышеприведенный случай - очень упрощенный пример, на практике у меня будут 3+ типа «MassivePOD» и многие типы DependantClass, которые могут быть зависимы друг от друга по-странным, поэтому мне нужна гибкость. (Типы всегда будут построены в некотором топологическом порядке, так что теоретически я всегда должен это делать) –

+0

Хмм, я не уверен, что вы можете это сделать. Конструктор кортежа не будет вызван до тех пор, пока все аргументы не будут оценены, так что «DependentClass» даже не имеет смысла указывать. Как насчет того, что вы создаете конструктор 'DependentClass', который пуст, и как только кортеж построен, вы вызываете функцию' .set() 'со ссылками на массивные POD? – Claudiu

+0

Да, это мое последнее средство. Есть способы обойти это, мне просто очень хотелось бы, чтобы мой конструктор выполнял свою работу, не имея при этом лишних слов о работе. –

Смежные вопросы