【问题标题】:Testing specific class name in different namespaces (SFINAE?)在不同的命名空间中测试特定的类名(SFINAE?)
【发布时间】:2014-03-17 08:07:39
【问题描述】:

假设我在不同的命名空间中有几个同名的类。

namespace A { class Foo { ... }; }
namespace B { class Foo { ... }; }
namespace C { class Foo { ... }; }

我想在编译时检测是否有名为 Foo 的类,尽管有命名空间。比如一些 SFINAE 类,

template <typename T> struct is_Foo {
  static const bool value = /* some magic here */;
}; 

然后我想is_Foo&lt;A::Foo&gt;::value 是真的,is_Foo&lt;B::Foo&gt;::value 是真的,is_Foo&lt;A::Bar&gt;::value 是假的。

然后,我想在static_assertstd::enable_if 中使用is_Foo。有可能吗?

【问题讨论】:

  • @Jarod42 不是真的。那个人在问“A 是否包含Foo”,而不是“这个类是A::Foo 吗?”

标签: c++ templates c++11 namespaces sfinae


【解决方案1】:
template <typename T>
struct is_Foo : public std::false_type {
};

template <>
struct is_Foo<A::Foo> : public std::true_type {
};

template <>
struct is_Foo<B::Foo> : public std::true_type {
};

template <>
struct is_Foo<C::Foo> : public std::true_type {
};

这通过使is_Foo&lt;T&gt; 继承自std::true_type 来实现T 的特化为A::Foo 等,并从std::false_type 继承任何其他T。如您所见,没有使用 SFINAE,仅使用继承和模板特化。

这可用于static_assertenable_if


如果您不想为每个班级专门设置is_Foo,则必须按照 cmets 中的建议使班级“合作”。一种方法是创建一个类并让所有这些类继承该类。该类可以有一个您可以检查的特殊成员,也可以只检查 T is_base_of 是否该类。

【讨论】:

  • 您还可以补充说,如果没有税收清单,不可能做到这一点(除非 Foo 类本身合作)。
  • 谢谢 (+1)。但是,我不喜欢每次创建一些新的Foo 类时都添加专业化is_Foo
  • @OlegSvechkarenko 没有办法。
  • @jrok - 谢谢。有什么原因吗?
  • 命名空间名称不能作为模板参数传递或推断的事实。它只是不是语言的一部分。
【解决方案2】:

本质上,这与@bolov 的答案中的方法相同,但符号略短。就个人而言,我发现这更易于阅读和维护,但当然,这是一个品味问题。 Code on ideone.com:

#include <iostream>
#include <type_traits>

namespace A { class Foo {}; }
namespace B { class Foo {}; class Bar {}; }
namespace C { class Foo {}; }

template<typename T>
struct is_Foo: public std::integral_constant<bool,
    std::is_same<T, A::Foo>::value || std::is_same<T, B::Foo>::value || 
    std::is_same<T, C::Foo>::value> {};

int main() {
 std::cout << is_Foo<A::Foo>::value << ' ' << is_Foo<B::Foo>::value << ' ' 
           << is_Foo<C::Foo>::value << ' ' << is_Foo<B::Bar>::value 
           << std::endl;

 return (0);
}

程序的输出:

1 1 1 0

【讨论】:

    【解决方案3】:

    以下内容基于 bolov 的回答。

    这就是我组织代码的方式 (live example):

    // declarations
    template<typename T>
    struct Foo_temp;
    
    namespace A {
       struct tag;
       using Foo = Foo_temp<tag>;
    }
    
    namespace B {
       struct tag;
       using Foo = Foo_temp<tag>;
    }
    
    namespace C {
       struct tag;
       using Foo = Foo_temp<tag>;
    }
    
    // definitions
    template<>
    struct Foo_temp<A::tag> { /*...*/ };
    
    template<>
    struct Foo_temp<B::tag> { /*...*/ };
    
    template<>
    struct Foo_temp<C::tag> { /*...*/ };
    
    // testing
    template<typename T>
    struct is_Foo : public std::false_type {};
    
    template<typename T>
    struct is_Foo<Foo_temp<T>> : public std::true_type {};
    

    这样,基于部分专业化的所有Foo 只有一个测试。当然,您可能会说,通过将声明与定义等分开,这比为每个命名空间专门化 is_Foo 需要更多的代码。但是,如果要定义的类型特征不仅仅是 is_Foo(比如从每个 Foo 中提取信息,或者对每个 Foo 应用类型转换),这种设计最终会得到回报(对我来说),因为对于每个特征有一个定义。

    Foo 之间的其他合作形式(如继承公共基数或声明特殊成员)相比,我发现这对编译器来说更轻量级。

    需要注意的是 Foo_test 特化需要在最初声明 Foo_test 的地方定义,即外部命名空间 A,B,C。因此,如果这些定义需要使用来自这些名称空间的信息,情况会更加复杂。在这种情况下,您可以求助于

    namespace A {
       struct tag;
       struct Foo_impl { /*...*/ };
       using Foo = Foo_temp<tag>;
    }
    
    //...
    
    template<>
    struct Foo_temp<A::tag> : A::Foo_impl {};
    

    在每个命名空间中都有实际的定义。

    标签?

    您现在可以清楚地看到Foo_temp 只是将tag 与实现(Foo_impl)相关联。这些标签实际上将命名空间转换为类型,以用作模板参数,因此您正在手动建模该语言不直接支持的东西。您可以将相同的标签用于其他类似的类定义,例如Bar

    事实上,如果您重新考虑您的应用程序,您可能会发现最终它只是您所追求的标签,而不是名称空间(但也许我错了)。我大量使用这种没有命名空间的设计:我有一个命名空间tag,我在其中定义了所有标签,所以我可以说例如

    template<typename T>
    struct array<tag::dense, T> { /*...*/ };
    
    template<typename T>
    struct array<tag::sparse, T> { /*...*/ };
    

    定义密集或稀疏数组表示,同时能够使用单个定义测试is_array。以here 为例。您甚至可以将标签视为 properties 并以特定顺序的组合或顺序使用它们,或者将它们制作为模板并添加参数(请参阅here)。标签可以根据上下文具有不同的含义,例如想象tag::lazy 定义一个数组、一个元组或一个函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-31
      • 1970-01-01
      • 2022-12-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多