【问题标题】:C++: Partially specializing template's type parameter as another template class's member-typeC++:部分特化的模板类型参数作为另一个模板类成员类型
【发布时间】:2016-07-29 06:24:58
【问题描述】:

我有一个模板结构SFoo,其中包含一个成员结构SZug

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

我有另一个结构体SBar,它带有一个类型参数:

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

我想将SBar 专门用于类型参数,使用SZug,如下所示:

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

这无法编译 - LLVM 输出:

不可演绎的模板参数'tTYPE'

虽然编译器可以根据需要轻松推断出这一点,但我猜只是 C++ 规范需要专门涵盖这种情况。

有什么办法可以做到吗?
(注意:我目前正在通过将SZug 移出SFoo 并使用using 声明来解决它,但这很丑陋。)

【问题讨论】:

  • 之后你打算做什么?我不清楚您期望“专业化”做什么。你能告诉我们如何声明一个非特化和特化SBar的变量吗?
  • 我实际上将这些纯粹用作特征类型,因此它们从未实际实例化。 Bar 只是为另一个(未提及的)类(加上其他一些东西)提供了一个 constexpr 成员指针。通常需要显式指定成员指针,但对于SZug 特化可以确定。 -- (最终,这与从 1 对 N 通用容器生成 N 对 N 通用嵌入式容器有关 - 成员指针指向用户的容器或节点信息“嵌入”(即成员变量)类型)。
  • 我可以稍微修改SZug 来检测它,使用 SFINAE 很容易做你想做的事情(见我的回答)。如果你不能,我不确定有没有办法检测到 tTYPESFoo&lt;T&gt;::SZug 类。

标签: c++ templates partial-specialization


【解决方案1】:

我不确定我是否完全理解您想要做什么,但您可以尝试以下操作(只需要向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&lt;T&gt;::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 { };

如果类型是SZug,则对SBar 模板进行轻微修改以启用“专业化”:

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&lt;T&gt;设置为SFoo&lt;T&gt;::SZug中的type属性,只需稍微修改std::is_same的第二个参数即可。

【讨论】:

  • 是的,这确实解决了问题,tyvm。对于我当前的问题,添加using type = tTYPE; 非常简单。 SBar 上的额外参数更烦人,但仍然可行。我目前正在决定这是否比我目前的修复更整洁。 - 如果明天之前没有更好的答案,我将把它打勾为已解决。干杯。
  • @xaxazak 在模板中添加Enabler这样的参数是常用的成语(大部分时候应该会自动推导出来),但是如果需要使用@987654336可能会出现问题@ 作为模板模板参数。如果你能详细说明为什么它很烦人,我也许可以提出一个更合适的解决方案?
  • 主要是模块化。 SBar 已经被频繁使用(在 1-to-N 容器中),并且不需要了解此新功能(N-to-N 容器)。但这都是我自己的代码,所以我可以更新它,并且它不会在任何地方用于模板模板的东西(尽管SZug 使用模板模板参数)。
  • @xaxazak 您只需要更新SBar 的“主”声明,您甚至可以简单地在任何内容之前添加前向声明。也许我遗漏了一些东西,但它不应该影响任何东西,因为 Enabler 参数是默认的。
  • 啊,是的,没错。但是,在实施了一些案例(例如:堆分配、自定义分配和用户管理的节点)之后,我发现将SZug 移到SFoo 之外最终会变得更整洁,因为它只需要完成一次并且它是 100 % 模块化的。但是,很高兴知道那里有哪些选择,也许这个问题可能对其他人有所帮助。 - 我确实想知道如果问题正文中的语法合法,C++ 是否会受益或受损。
【解决方案2】:

您可以通过以下方式获得您正在查看的效果(打印出 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 一个普通类基:

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,而是专门化了基类。不过,这有效地产生了相同的效果。

【讨论】:

  • 这适用于SBar&lt;SFoo&lt;int&gt;&gt; b1;。不幸的是,我正在寻找SBar&lt;SFoo&lt;int&gt;:: SZug &gt; b1;(AFAICT 仍会打印 0)。
  • @xaxazak 你是对的 - 我错过了问题中的那个细节,但原则适用。答案细节的一些小改动使其适用于您的原始意图。要点是:1. 一个非模板的“详细”基类,以及 2. 根据它是否可以转换为这个基类来特化一个基类。
  • 是的,这确实解决了问题。我正在交叉手指,可能有一种更整洁的方法来做到这一点。您的解决方案和 Holt 的解决方案都可以接受,但恕我直言,我认为添加一个固定器成员类型(Holt 的方法)比从固定器结构继承要好一些。
  • 当然,您应该接受任何您认为最好的答案(我也会阅读他的答案 - 学习一些新想法总是很好)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-06
  • 1970-01-01
  • 2011-07-03
相关资源
最近更新 更多