Проблема в вашем коде состоит в том, что руководства по выводам, представленные в C ++ 17, работают только при выводе всех аргументов шаблона.
Так зовут
node page2{2, tag<html>{none}};
работает потому что
(1) tag<html>{none}
не требует вывода шаблона, потому что первый параметр шаблона объяснен там, где список переменных (Tags...
) пуст (без аргументов после none
), поэтому tag
является tag<html>
и
(2) автоматические инструкции по выводу для node
выводить все аргументы шаблона (Tags...
), поэтому page2
выводится как node<tag<html>>
.
Проблема возникает при написании
tag<span>{none, tag<h1>{none}}
потому что для tag<span>
после none
есть аргумент, поэтому список переменных Tags...
не пуст, но не может быть (автоматически, с помощью неявных руководств по выводам), потому что вы объяснили первый шаблон аргумент (span
).
Очевидно, что вы можете решить эту проблему, добавив функцию make_tag()
, как предложил Круз Джин, но я предлагаю вам другое решение, использующее автоматические направляющие вычитания.
Прежде всего, определите класс оболочки w
для tag_name
s
template <tag_name>
struct w
{ };
затем переписать ваш tag
класс с помощью two constructor; первый для случая с пустым внутренним tags
explicit tag (attribute_type atts)
: attributes{std::move(atts)}
{ }
второй для общего случая (также не пустой внутренний список tags
), который получает элемент w<T>
, который разрешает автоматический вычет также для T
explicit tag (w<T>, attribute_type atts, Tags... tggs)
: attributes{std::move(atts)}, tags{tggs...}
{ }
Первый конструктор, разрешающий поддерживать формат
tag<html>{none}
в случае отсутствия содержащихся тегов; второй разрешает этот тип tag
объявлений объектов
tag{w<html>{}, none}
tag{w<span>{}, none, tag<h1>{none}}
Ниже приведен полный пример компиляции
#include <map>
#include <string>
#include <tuple>
#include <utility>
namespace web
{
enum class attrs
{ charset, name, content, http_equiv, rel, href, id, src, lang };
using attribute = std::pair<attrs, std::string>;
using attribute_type = std::map<attrs, std::string>;
const auto none = attribute_type{};
enum tag_name
{ html, head, meta, title, link, body, div, script, plain, p, h1, span };
template <typename... Tags>
struct node
{
int increment;
std::tuple<Tags...> tags;
explicit node (int const incr, Tags ... tggs)
: increment{incr}, tags{tggs...}
{ }
};
template <tag_name>
struct w
{ };
template <tag_name T, typename ... Tags>
struct tag
{
attribute_type attributes;
std::tuple<Tags...> tags;
explicit tag (attribute_type atts)
: attributes{std::move(atts)}
{ }
explicit tag (w<T>, attribute_type atts, Tags... tggs)
: attributes{std::move(atts)}, tags{tggs...}
{ }
};
template <>
struct tag<plain>
{
std::string content;
explicit tag (std::string val) : content{std::move(val)}
{ }
};
} // namespace web
int main ()
{
using namespace web;
node page1{2};
node page2{2, tag<html>{none}};
node page3{2, tag<html>{{{attrs::lang, "en"}}}};
node page4{2, tag<html>{{{attrs::name, "viewport"},
{attrs::content, "width=device-width, initial-scale=1.0"}}}};
node page5{2, tag<head>{none}, tag<body>{none},
tag<plain>{"Hello World"}};
node page6{1, tag{w<span>{}, none, tag<h1>{none}}};
}