3

我真的不知道该怎么办。根据变量(或其类型)的来源,代码的行为似乎有所不同。而且我不知道如何解决这个问题以获得一致的行为。

下面(和链接)是为fmtster开发的一些代码的提炼版本。

在此代码中,为特定类型(在示例中)float创建了专门的结构。std::string然后框架({fmt} 或 )调用模板化结构中的一些成员函数(示例中未显示std::format)。

is_foobar_v<>旨在检查对于指定类型是否存在作为其基类的foo结构的特化。bar当特定类型用作模板参数时,它可以正常工作。此外,当decltype()与局部变量一起使用时,它也可以正常工作:

  • is_foobar_v<int>返回false,因为foo<int> 不用bar基类
  • is_foobar_v<float>返回true,因为foo<float> 确实用作bar基类。
  • is_foobar_v<string>返回true,因为foo<string> 确实用作bar基类。

(显然使用decltype()不是问题。)

但是,代码中的下一个示例来自两个is_foobar_v<>失败的地方。下面的代码逐步遍历 a 的元素tuple<>,检查foo<>使用该类型作为模板参数的结构是否基于bar. is_foobar_v<>总是返回false

请注意,我还添加了foo<>对象的显式实例化,以显示调用了哪些构造函数。您会注意到这些日志显示代码没有在 lambda 中实例化正确的特化。奇怪的是,在实际代码中,即使我的检查失败,实例化实际上也能正常工作。但是,我无法弄清楚为什么它无法在这个提炼的代码中实例化正确的专业化结构。在浪费了一天的大部分时间之后,我希望这与检查失败无关。

我想重申,这是我遇到这种情况的两个地方之一。另一个是在调用访问者函数的更简单的模板化成员函数中,它与tuple<>循环无关,也没有使用 lambda。在那种情况下,我能够解决这个问题,因为所讨论的类型可以显式地作为模板参数(或者更具体地作为模板参数的类型特征(即T::value_type))。

该解决方法在此处不可用。

此外,正如我所提到的,在实际代码中,正在创建正确的专业化。所以在那里,我还尝试检查是否存在仅存在于bar等效项中的函数。我尝试了测试程序下面的代码片段,它也没有工作。我试图实际实例化一个foo<>对象,然后实例化dynamic_cast<>它指向一个指针的bar指针,我会检查nullptr. 但是在那种环境中,我似乎不允许foo<>明确地创建对象。

无论如何,上代码:

godbolt.org 上的代码

#include <iostream>
using std::cout;
using std::endl;
using std::boolalpha;
#include <string>
using std::string;
#include <tuple>
using std::make_tuple;

// template argument iteration for std::tuple<>
template<typename F, typename... Ts, std::size_t... Is>
void ForEachElement(const std::tuple<Ts...>& tup,
                    F fn,
                    std::index_sequence<Is...>)
{
    (void)(int[]) { 0, ((void)fn(std::get<Is>(tup)), 0)... };
}
template<typename F, typename...Ts>
void ForEachElement(const std::tuple<Ts...>& tup, F fn)
{
    ForEachElement(tup, fn, std::make_index_sequence<sizeof...(Ts)>());
}

template<typename T>
struct foo
{
    foo() { cout << "foo::foo<>()" << endl; }
};

struct bar
{
    bar() { cout << "bar::bar()" << endl; }
};

template<>
struct foo<float> : public bar
{
    foo() { cout << "foo::foo<float>()" << endl; }
};

template<>
struct foo<string> : public bar
{
    foo() { cout << "foo::foo<string>()" << endl; }
};


template<typename T>
inline constexpr bool is_foobar_v = std::is_base_of_v<bar, foo<T> >;

int main()
{
    int i;
    float f;
    string s;

    foo<int>();
    cout << "foo<" << typeid(int).name() << "> "
         << (is_foobar_v<int> ? "IS" : "is NOT")
         << " a child of bar" << endl;

    foo<float>();
    cout << "foo<" << typeid(float).name() << "> "
         << (is_foobar_v<float> ? "IS" : "is NOT")
         << " a child of bar" << endl;

    foo<string>();
    cout << "foo<" << typeid(string).name() << "> "
         << (is_foobar_v<string> ? "IS" : "is NOT")
         << " a child of bar" << endl;

    foo<decltype(i)>();
    cout << "foo<" << typeid(i).name() << "> "
         << (is_foobar_v<decltype(i)> ? "IS" : "is NOT")
         << " a child of bar" << endl;

    foo<decltype(f)>();
    cout << "foo<" << typeid(f).name() << "> "
         << (is_foobar_v<decltype(f)> ? "IS" : "is NOT")
         << " a child of bar" << endl;

    foo<decltype(s)>();
    cout << "foo<" << typeid(s).name() << "> "
         << (is_foobar_v<decltype(s)> ? "IS" : "is NOT")
         << " a child of bar" << endl;

    auto tup = make_tuple(i, f, s);
    ForEachElement(
        tup,
        [](const auto& elem)
        {
            foo<decltype(elem)>();
            cout << "foo<" << typeid(elem).name() << "> "
                 << (is_foobar_v<decltype((elem))> ? "IS" : "is NOT")
                 << " a child of bar" << endl;
        });
}

输出如下所示:

foo::foo<>()
foo<i> is NOT a child of bar
bar::bar()
foo::foo<float>()
foo<f> IS a child of bar
bar::bar()
foo::foo<string>()
foo<NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE> IS a child of bar
foo::foo<>()
foo<i> is NOT a child of bar
bar::bar()
foo::foo<float>()
foo<f> IS a child of bar
bar::bar()
foo::foo<string>()
foo<NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE> IS a child of bar
foo::foo<>()
foo<i> is NOT a child of bar
foo::foo<>()
foo<f> is NOT a child of bar
foo::foo<>()
foo<NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE> is NOT a child of bar

我尝试的另一件事:

template<typename T>
constexpr bool HasBarBaseHelper()
{ return false; }
template<>
constexpr bool HasBarBaseHelper<bar>()
{ return true; }

template<typename T>
constexpr bool HasBarBase()
{ return HasBarBaseHelper<fmt::formatter<T> >(); }

...

if (HasBarBaseHelper<foo<decltype(elem)> >())
{
...

我真的不明白为什么decltype()某些变量的检查成功,而其他变量则不然。正如您从输出中看到的那样,类型似乎完全相同。

任何帮助表示赞赏。

瑞克

4

0 回答 0