2016-07-29 127 views
2

我有一个模板结构SFoo包含的成员结构SZugC++:部分专业模板的类型参数作为另一个模板类的成员类型

template <typename tTYPE> 
struct SFoo 
    { 
    struct SZug {}; 
    }; 

我有另一种结构SBar,需要一个类型参数:

template <typename tTYPE> 
struct SBar 
    { /* stuff */ }; 

我想使用SZug为类型参数专门SBar,像这样:

template <typename tTYPE> 
struct SBar<typename SFoo<tTYPE>::SZug> 
    { /* different stuff */ }; 

这不会编译 - LLVM输出:

非抵扣模板参数“TTYPE”

虽然编译器可以很容易地推断出这一点,如果它愿意,我猜这是只是C++规范需要专门涵盖这种情况。

有什么办法可以达到这个目的吗?
(注:我目前工作围绕它通过移动SZugSFoo和使用using声明,但它的丑陋)

+0

你打算做什么之后?我不清楚你期望“专业化”做什么。你能告诉我们你将如何声明一个非专业化和专业化的SBar变量吗? – Holt

+0

我实际上将这些纯粹用作特征类型,所以它们从未实际实例化过。 Bar简单地为另一个(未提及的)类提供一个constexpr成员指针(以及其他一些东西)。通常情况下,成员指针需要明确指定,但对于“SZug”专业化可以确定。 - (最终这与从1到N通用容器生成N对N通用嵌入容器有关 - 成员指针指向用户的容器或节点信息“嵌入”(即成员变量)类型)。 – xaxazak

+0

我可以稍微修改一下'SZug'来检测它,用SFINAE做你想做的事很容易(见我的答案)。如果你不能,我不确定是否有办法检测到'tTYPE'是'SFoo :: SZug'类。 – Holt

回答

2

我不知道我完全理解你想做的事,但你可以尝试以下的(只需要添加特定的属性SZug

template <typename tTYPE> 
struct SFoo { 
    struct SZug { 
     // Add this to be able to obtain SFoo<T> from SFoo<T>::SZug 
     using type = tTYPE; 
    }; 
}; 

再小的模板来检查,如果一个类型是SFoo<T>::SZug

template <typename tTYPE, typename Enabler = void> 
struct is_SZug: public std::false_type { }; 

template <typename tTYPE> 
struct is_SZug<tTYPE, typename std::enable_if< 
    std::is_same<tTYPE, typename SFoo<typename tTYPE::type>::SZug>{} 
>::type>: public std::true_type { }; 

和轻微修改的SBar模板,使“专业化”,如果该类型是SZug

template <typename tTYPE, typename Enabler = void> 
struct SBar 
    { static void g(); }; 

template <typename tTYPE> 
struct SBar<tTYPE, typename std::enable_if<is_SZug<tTYPE>{}>::type> 
    { static void f(); }; 

小检查:

void f() { 
    SBar<int>::g(); 
    SBar<SFoo<int>::SZug>::f(); 
} 

注:您也可以直接设置SFoo<T>type属性在SFoo<T>::SZug,你只需要改变的std::is_same一点点的第二个参数。

+0

是的,这确实解决了问题,tyvm。对于我目前的问题,添加'using type = tTYPE;'非常简单。 'SBar'上的额外参数更烦人,但仍然可以。我目前正在确定这是否比我目前的修复更加整洁。 - 如果明天没有更好的答案,我会打勾解决。干杯。 – xaxazak

+0

@xaxazak这是一个常用的习惯用法,将这样的'Enabler'参数添加到模板中(大多数情况下应该自动推导出这个参数),但如果您需要使用'SBar'作为模板模板参数,则可能会出现问题。也许如果你能详细说明为什么它很烦人,我可能会提出一个更合适的解决方案? – Holt

+0

主要是模块化。 SBar已经被频繁使用(在1到N容器中),并且不需要知道这个新功能(N到N个容器)。但它是我自己的代码,所以我可以更新它,并且它不用于任何地方的模板模板内容(尽管'SZug'使用模板参数)。 – xaxazak

1

你可以得到效果为您正在寻找通过以下(打印出0 1,顺便说一句):

#include <type_traits> 
#include <iostream> 

namespace detail 
{ 
    struct SZugBase{}; 
} 

template <typename tTYPE> 
struct SFoo                                 
{ 
    struct SZug : public detail::SZugBase {}; 
}; 

template<typename tType, bool IsFoo> 
struct SBarBase 
{ 
    int value = 0; 
}; 

template<typename tType> 
struct SBarBase<tType, true> 
{ 
    int value = 1; 
}; 

template <typename tTYPE> 
struct SBar : public SBarBase<tTYPE, std::is_convertible<tTYPE, detail::SZugBase>::value> 
{ /* stuff */ }; 

int main() 
{ 
    SBar<int> b0; 
    SBar<SFoo<int>::SZug> b1; 

    std::cout << b0.value << " " << b1.value << std::endl; 
} 

说明

首先,我们得到一个SZug常规b类ASE:

namespace detail 
{ 
    struct SZugBase{}; 
} 

template <typename tTYPE> 
struct SFoo            
{ 
    struct SZug : public detail::SZugBase {}; 
}; 

注意以下几点:

  1. SZugBase不受任何参数,所以很容易独立地指的SFoo

  2. SZugBase参数来它是在一个detail命名空间,所以,按照常见的C++约定,你告诉客户端你的代码忽略它。

现在我们给SBar两个基类,专业上的东西是否可以转换为的SZug非模板的基础:

template<typename tType, bool IsFoo> 
struct SBarBase 
{ 
    int value = 0; 
}; 

template<typename tType> 
struct SBarBase<tType, true> 
{ 
    int value = 1; 
}; 

最后,我们只需要做出SBar这些基地的子类(取决于专业化):

template <typename tTYPE> 
struct SBar : public SBarBase<tTYPE, std::is_convertible<tTYPE, detail::SZugBase>::value> 
{ /* stuff */ }; 

请注意,你不擅长SBar她e,你更专门化基类。不过,这实际上会产生相同的效果。

+0

适用于'SBar > b1;'。不幸的是,我正在寻找'SBar ::'**'SZug' **'> b1;'(AFAICT仍然会打印0)。 – xaxazak

+0

@xaxazak你是对的 - 我错过了这个问题的细节,但这个原则适用。答案细节的一些细微变化适用于您的原意。要点如下:1.非模板“细节”基础,2.根据是否可以转换为基础来专门设计基类。 –

+0

是的,这确实解决了这个问题。我正在越过我的手指可能会有一个更整洁的方式来做到这一点。你的解决方案和Holt的都可以被接受,但恕我直言,我认为增加一个修补程序成员类型(Holt的方法)比从修复程序结构继承更好。 – xaxazak