C++ 在模板类中标识类的元函数有问题

C++ 在模板类中标识类的元函数有问题,c++,templates,template-meta-programming,boost-mpl,C++,Templates,Template Meta Programming,Boost Mpl,我有以下结构的代码: template <typename T> struct Foo { struct Bar { int data; }; }; 奇怪的是,clang将编译代码,但它会发出警告,并且元函数不起作用(始终为false): main.cpp:38:8:警告:类模板部分专门化包含无法推导的模板参数; 这种局部专门化永远不会被使用 结构为:boost::mpl::true_ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ main.cpp:

我有以下结构的代码:

template <typename T>
struct Foo
{
  struct Bar
  {
    int data;
  };
};
奇怪的是,clang将编译代码,但它会发出警告,并且元函数不起作用(始终为false):

main.cpp:38:8:警告:类模板部分专门化包含无法推导的模板参数;
这种局部专门化永远不会被使用
结构为:boost::mpl::true_
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:37:20:注意:不可推断的模板参数“T”
模板
^

这个问题有解决办法吗?特定于c++11的解决方案就可以了。

问题在于
T
是类型名称的一部分
Foo::Bar
,但它不是类型结构的一部分

一种可能的解决方案是在类型的结构中编码
T

template<typename Outer, typename Inner> struct Nested: public Inner {
  using Inner::Inner;
};
template<typename T> struct Foo {
  struct BarImpl {
    int data;
  };
  using Bar = Nested<Foo<T>, BarImpl>;
};

template <typename T> struct is_bar: std::false_type {};
template <typename T, typename U> struct is_bar<Nested<Foo<T>, U>>:
  std::is_same<typename Foo<T>::Bar, Nested<Foo<T>, U>> {};
模板结构嵌套:公共内部{
使用Inner::Inner;
};
模板结构Foo{
结构BarImpl{
int数据;
};
使用条=嵌套;
};
模板结构是_-bar:std::false _-type{};
模板结构是\u栏:
std::is_same{};
测试:

static_assert(is_bar<Foo<int>::Bar>::value, "!");
static_assert(!is_bar<Foo<int>>::value, "!");
static_assert(!is_bar<int>::value, "!");
static_断言(is_bar::value,“!”;
静态断言(!is_bar::value,“!”);
静态断言(!is_bar::value,“!”);

编译器是正确的,简单易懂的解释是:他们只是不想替换所有可能的类型
t
,只是为了意识到给定模板中有一个嵌套的类型栏。在一本关于模板的“经典”(我希望也是广为人知的)书中可以找到更精确的解释:“C++模板-完整指南”

幸运的是,C++11帮助您做得更好

将输出:

zaufi@gentop /work/tests $ ./has-nested-bar
1
0

稍后,您可以将此元函数与
is_foo
结合起来,检查嵌套的
Bar
是否确实位于
foo
内部。

下面是一个非常不雅的解决方案,使用TTI来解决我自己的问题(http://svn.boost.org/svn/boost/sandbox/tti):

首先,将虚拟标记添加到条形图:

template <typename T>
struct Foo
{
  struct Bar
  {
    typedef void i_am_bar;
    int data;
  };
};
模板
结构Foo
{
结构条
{
typedef void i_am_bar;
int数据;
};
};
接下来,使用TTI检查该标记:

BOOST_TTI_HAS_TYPE(i_am_bar);

template <typename T>
struct is_bar : boost::tti::has_type_i_am_bar<T>
{};
...
BOOST_MPL_ASSERT(( is_bar<Foo<int>::Bar> ));
BOOST_MPL_ASSERT_NOT(( is_bar<Foo<int> > ));
BOOST_MPL_ASSERT_NOT(( is_bar<int> ));
BOOST\u TTI\u有\u类型(i\u am\u条);
模板
struct is_bar:boost::tti::has_type_i_am_bar
{};
...
BOOST_MPL_ASSERT((is_bar));
BOOST_MPL_ASSERT_NOT((is_bar));
BOOST_MPL_ASSERT_NOT((is_bar));

当然很恶心,但它满足了我的用例。

正如Clang所说,
typename Foo::Bar
是一个不可推断的上下文。这与要求编译器枚举
Foo
的所有可能参数相同,并检查是否有
Foo::Bar
与提供的
T
匹配。另外,
template void f(typename Foo::bar){}
是同一种不可推断的上下文,您必须手动指定
T
才能调用此函数。(顺便说一句,这是如何指定类模板的部分专门化。)谢谢,您的解释很有意义。现在的问题是构建一个变通方法。这很聪明——可能太聪明了。我担心任何对这段代码进行维护的人(阅读:future me)将很难解开它。我有一个使用boost::tti的替代解决方案,但我的声誉太低,无法在8小时内回答自己的问题。所以你们都得等到今晚:)目的是测试一个类型是否是嵌套的
,而不是它是否有。@zaufi-谢谢,但正如Xeo所说,这不是我要找的。然而,我的“丑陋”解决方案使用了这种类型的实用程序。你并不需要Boost.TTI来实现这一点,编写一个trait来检查嵌套的typedef是否存在(the
has_element_type
struct)。不过,在找到解决办法方面做得不错。
static_assert(is_bar<Foo<int>::Bar>::value, "!");
static_assert(!is_bar<Foo<int>>::value, "!");
static_assert(!is_bar<int>::value, "!");
#include <type_traits>

template <typename T>
struct has_nested_bar
{
    template <typename W>
    struct wrapper {};

    template <typename C>
    static std::true_type check(
        const wrapper<C>*
      , const typename C::Bar* = nullptr
      );

    template <class C>
    static std::false_type check(...);

    constexpr static bool value = std::is_same<
        decltype(check<T>(nullptr))
      , std::true_type
      >::type::value;
    typedef std::integral_constant<bool, value> type;
};
template <typename T>
struct Foo
{
    struct Bar
    {
        int data;
    };
};

struct Bar {};

int main()
{
    std::cout << has_nested_bar<Foo<int>>::value << std::endl;
    std::cout << has_nested_bar<Bar>::value << std::endl;
    return 0;
}
zaufi@gentop /work/tests $ ./has-nested-bar
1
0
template <typename T>
struct Foo
{
  struct Bar
  {
    typedef void i_am_bar;
    int data;
  };
};
BOOST_TTI_HAS_TYPE(i_am_bar);

template <typename T>
struct is_bar : boost::tti::has_type_i_am_bar<T>
{};
...
BOOST_MPL_ASSERT(( is_bar<Foo<int>::Bar> ));
BOOST_MPL_ASSERT_NOT(( is_bar<Foo<int> > ));
BOOST_MPL_ASSERT_NOT(( is_bar<int> ));