【问题标题】:How to alllow template class constructor to support different number of arguments depending on base type?如何允许模板类构造函数根据基类型支持不同数量的参数?
【发布时间】:2020-01-17 17:25:40
【问题描述】:

我有类似下面的代码:

class CIntColumn : public CColumn
{
public:
    CIntColumn(LPCTSTR pszName)
    {
    }
    // Etc.
}

class CTextColumn : public CColumn
{
public:
    CTextColumn(LPCTSTR pszName, int nMaxLength)
    {
    }
    // Etc.
}

template<typename BASE_TYPE>
CNullableColumn : public BASE_TYPE
{
public:
    // Questions about code that goes here
}

class CNullableIntColumn : public CNullableColumn<CIntColumn>
{
public:
    CNullableIntColumn(LPCTSTR pszName)
        : CNullableColumn<CIntColumn>(pszName)
    {
    }
}

class CNullableTextColumn : public CNullableColumn<CTextColumn>
{
public:
    CNullableTextColumn(LPCTSTR pszName, int nMaxLength)
        : CNullableColumn<CTextColumn>(pszName, nMaxLength)
    {
    }
}

因为CNullableIntColumn 需要将一个参数传递给基类,而CNullableTextColumn 需要将两个参数传递给基类,所以这段代码不会编译。

有什么技巧可以修改我的CNullableColumn&lt;&gt; 模板类,使其支持这两种情况吗?理想情况下,我可以使用某种条件编译,但 C++ 似乎不支持这里需要的那种。

【问题讨论】:

  • 您希望CNullableColumn 将某个常量传递给int nMaxLength,还是需要一个额外的构造函数参数?
  • @HolyBlackCat:我希望它将构造函数参数传递给CTextColumn。抱歉,更新了代码。
  • 也许您可以继承构造函数而不是手动编写它们? (CNullableColumn 中的using BASE_TYPE::BASE_TYPE,派生类中的using CNullableColumn&lt;blah&gt;::CNullableColumn)这只有在您的实际代码中CNullable*Column 构造函数除了调用父母的构造函数之外什么都不做时才有效。

标签: c++ templates inheritance constructor


【解决方案1】:

您需要一个通用的转发构造函数,它将接受任意数量的参数并将它们传递给CNullableColumn 的基类:

template<typename BASE_TYPE>
class CNullableColumn : public BASE_TYPE {
public:
    template<typename ... Args>
    CNullableColumn(Args && ... args) :
        BASE_TYPE(std::forward<Args>(args)...) {}
};

这应该会导致您以后对CNullableTextColumnCNullableIntColumn 的所有使用将它们的参数正确地转发到它们的根。


因此,如果您事先知道每个类型将只有一个参数,除了一个或两个特定类型将有两个之外,您可能更喜欢模板特化:

template<typename BASE_TYPE>
class CNullableColumn : public BASE_TYPE {
public:
    CNullableColumn(LPCTSTR pszName) :
        BASE_TYPE(pszName) {}
};

//Specialization
template<>
class CNullableColumn<CTextColumn> : public CTextColumn {
public:
    CNullableColumn(LPCTSTR pszName, int maxLength) :
        CTextColumn(pszName, maxLength) {}
};

//Could create other specializations for other types with different #s of arguments, if needed

现在,模板专业化的缺点是您必须巧妙地在实现中避免重复代码。但它至少可以优雅地解决您正在尝试处理的问题,而不会将所有内容都变成 Varargs 的噩梦。

【讨论】:

  • 好吧,这很有道理。但我有许多不同类型的CNullableColumns。只有CNullableTextColumn 有两个参数。对于派生自CNullableColumn 的每种类型,我是否还需要一个构造函数?
  • 其实,如果我理解正确的话,我只需要为每个参数类型和计数创建一个。让我看看我能不能弄清楚语法。
  • @JonathanWood 您定义的每个类都需要指定自己的构造函数。您可能能够对那些其他类使用相同的“完美可变参数转发”,但如果您稍后编写不正确尊重预期 API 的代码,则可能会导致一些极其难看的错误消息。哎呀,您可能只需要放弃 CNullableXColumn 并使用 CNullableColumn&lt;X&gt; 来获取您需要的所有参考资料。
  • 我不认为我可以做你的最后一个建议,因为每个版本都可以有专门的代码。如果我所有的派生类都只传递一个参数,那么看起来我的模板类中只需要一个构造函数。 (是的,每个派生类都有自己的构造函数。)而且我还没有完全理解两个版本所需的语法。 Args 是什么?
  • @JonathanWood 嗯。也许更好的候选者是模板专业化。这与您的原始问题有点正交,但我会将其添加到我的答案中。
【解决方案2】:

以 Xirema 似乎最初的建议为基础,以下似乎可行。

template <typename BASE_TYPE>
class CNullableColumn : public BASE_TYPE
{
protected:
    template<typename T1>
    CNullableColumn<BASE_TYPE>(T1 pszName)
        : BASE_TYPE(pszName)
    {
    }

    template<typename T1, typename T2>
    CNullableColumn<BASE_TYPE>(T1 pszName, T2 nMaxLength)
        : BASE_TYPE(pszName, nMaxLength)
    {
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多