Итак, мы все были там. У меня есть хороший объект, у меня есть конструктор для него, объект может разрешить true, если он создан правильно, но у него также есть другое состояние, поэтому я могу с радостью написать:
if (const auto& str = std::ofstream("/tmp/myfile"))
{
str << "Oh that was easy\n";
}
Но что бы я хотелочень нравится делать, это попробовать несколько альтернатив:
if (const std::stream& str = (std::ofstream("/tmp/myfile") || std::ofstream("/temp/myfile") || std::ofstream("./myfile")) )
{
str << "Oh that was not so easy\n";
}
Очевидно, нет! Я потенциально открыл 3 файла, но привел их к «истинному» bool, и, конечно, не смог вернуться к std :: stream. Если я перегрузлю operator ||
для потока rvals, то он откроет все 3 файла, потому что ленивая оценка забыта!
Я знаю, что std :: stream, возможно, плохой пример, но спустя 20 страниц я закончу объяснение моего опционального процессора строк, поэтому давайте придерживаться std :: stream.
Что яможно было бы ожидать, что возможно будет что-то вроде:
if (const std::stream str = (std::piecewise_construct(std::ofstream,"/tmp/myfile") || std::piecewise_construct(std::ofstream,"/temp/myfile") || std::piecewise_construct(std::ofstream,"./myfile")) )
{
str << "Oh that maybe makes sense?\n";
}
Чтобы сделать это, похоже, мне может понадобиться 2 перегрузки operator ||
, одна из которых принимает две нерешенные piecewise_construct и разрешает левую для начала,и другой, который принимает объект rval слева и оценивает правую часть только в случае, если он «провален». Что-то вроде:
template<class PC2>
std::stream operator ||(std::stream&& str, const PC2& pc2)
{
if (str)
return std::move(str);
return pc2.construct();
}
template<class PC1, class PC2>
std::stream operator ||(const PC1& pc1, const PC2& pc2)
{
return operator||(pc1.construct(), pc2);
}
Where PCx here is the piecewise construct.
Но я сразу вижу, что std :: peicewise_construct - это просто тег. Сам по себе он не имеет никакой функциональности. Также я борюсь за то, чтобы найти какой-либо способ заставить эту работу работать с ограничениями шаблона FINAE: Нет особого смысла в создании двух несвязанных типов, которые нельзя перемещать. В C ++ существует множество подобных «хитростей», таких как std :: function или lambdas, которые пытаются выполнить аналогичную работу?
Прямо сейчас мне бы хотелось решение в C ++ 11, которое нене похоже на Франкенштейна после драки с его монстром, но не чувствуйте себя ограниченным, если нужная мне функция - всего лишь версия!
Между тем у меня есть 3 копии в основном одного и того же парсера и хранилища значений аргумента, потому чтоклиенты не могут договориться о том, какую опцию следует вызывать между версиями.
Полагаю, мне нужен какой-то настраиваемый шаблон, а не что-то жестко привязанное к конкретному классу, в данном случае std :: stream, ичетный поток имеет множество конструкторов. Достаточно распространенным шаблоном с дескрипторами является код, который в основном хочет сделать h = (open(O_APP) || open(O_CREATE) || open(O_TRUNC))
{за исключением того, что это C, а не объекты}.
Одним из способов продвижения вперед является некоторый шаблонный класс, который содержит std :: bind и знает, как создать, в свою очередь, созданный из вспомогательной функции. И, возможно, тип этого объекта обеспечит защиту типов, которую я тоже желаю.
И еще одно замечание ... В этом примере я использую std :: istream только с одним аргументом, но, конечно же, конструкторомfor istream может принимать несколько аргументов и иметь несколько альтернативных конструкторов, а мой внутренний сценарий использования имеет как минимум 3 и, возможно, больше опций в будущем, поэтому он действительно должен поддерживать тот шаблонный список произвольных аргументов, который нам всем нравится.