根据一些枚举值在编译时添加额外的方法
Add extra methods at compile time according to some enum value
假设我有以下 class:
class Extra final
{
public:
void Method1() const {std::cout << "Method 1" << std::endl;}
void Method2() const {std::cout << "Method 2" << std::endl;}
};
我为一些classT
创建了一个class模板,可以根据不同的场景在编译时使用Extra
enable/disable方法(在枚举中列出):
enum class Scenario
{
None, // No extra.
One, // Only Method1 allowed.
Two, // Only Method2 allowed.
Both // Both Method1 and Method2 allowed.
};
class 模板如下所示:
template<typename T, Scenario R>
class ExtraAdder : public T
{
};
template<typename T>
class ExtraAdder<T, Scenario::One> : public T
{
public:
void Method1Extra() const {m_extra.Method1();}
private:
Extra m_extra;
};
template<typename T>
class ExtraAdder<T, Scenario::Two> : public T
{
public:
void Method2Extra() const {m_extra.Method2();}
private:
Extra m_extra;
};
template<typename T>
class ExtraAdder<T, Scenario::Both> : public T
{
public:
void Method1Extra() const {m_extra.Method1();}
void Method2Extra() const {m_extra.Method2();}
private:
Extra m_extra;
};
可以如下使用:
class Base
{
public:
virtual ~Base() = default;
void BaseMethod() const {std::cout << "Base method" << std::endl;}
// Some other stuff...
};
int main(int argc, char **argv)
{
std::cout << "Scenario: None" << std::endl;
ExtraAdder<Base, Scenario::None> none;
none.BaseMethod();
//none.Method1Extra(); // Does not compile.
//none.Method2Extra(); // Does not compile.
std::cout << std::endl << "Scenario: One" << std::endl;
ExtraAdder<Base, Scenario::One> one;
one.BaseMethod();
one.Method1Extra();
//one.Method2Extra(); // Does not compile.
std::cout << std::endl << "Scenario: Two" << std::endl;
ExtraAdder<Base, Scenario::Two> two;
two.BaseMethod();
//two.Method1Extra(); // Does not compile.
two.Method2Extra();
std::cout << std::endl << "Scenario: Both" << std::endl;
ExtraAdder<Base, Scenario::Both> both;
both.BaseMethod();
both.Method1Extra();
both.Method2Extra();
return 0;
}
重点是,通过更改模板参数 Scenario
,我只能访问包装的 m_extra
成员的某些方法,这正是我想要的。然而,正如您所看到的,不同的专业非常冗长(并且有一些重复)。
问题:有没有一种方法可以产生完全相同的模板特化,但以一种不太冗长的方式,重复有限或没有重复?
我发现最接近解决这个问题的是 this question(使用 std::enable_if
),但我无法根据我的情况调整它。
我正在使用符合 C++17 标准的 g++。
您可以使用 enable_if
但您还需要制作成员函数模板函数以便检查可以依赖于某些东西:
template<typename T, Scenario R>
class ExtraAdder : public T {
Extra m_extra;
public:
template<bool B = true>
std::enable_if_t<B && (R == Scenario::One || R == Scenario::Both)>
Method1Extra() const {
m_extra.Method1();
}
template<bool B = true>
std::enable_if_t<B && (R == Scenario::Two || R == Scenario::Both)>
Method2Extra() const {
m_extra.Method2();
}
};
如果您不需要 SFINAE 了解这些方法是否存在,您可以简单地 static_assert
:
template<typename T, Scenario R>
class ExtraAdder : public T {
Extra m_extra;
public:
void Method1Extra() const {
static_assert(R == Scenario::One || R == Scenario::Both);
m_extra.Method1();
}
void Method2Extra() const {
static_assert(R == Scenario::Two || R == Scenario::Both);
m_extra.Method2();
}
};
或者,您可以将 enable_if
放入模板而不是 return 类型声明。我个人觉得这更具可读性,它的优点是可以使用 return 类型推导。
template<typename T, Scenario R>
class ExtraAdder : public T {
Extra m_extra;
public:
template<
Scenario S = R,
typename = std::enable_if_t<(S == Scenario::One || S == Scenario::Both)>
>
void Method1Extra() const {
m_extra.Method1();
}
template<
Scenario S = R,
typename = std::enable_if_t<(S == Scenario::Two || S == Scenario::Both)>
>
decltype(auto) Method2Extra() const {
return m_extra.Method2();
}
};
实例here.
将 enable_if
与成员函数一起使用的一个常见陷阱是,它仅在模板参数的替换格式不正确时才有效。因此,我们需要添加额外的模板参数 Scenario S = R
(或另一个答案中的 bool
),以便将替换推迟到使用该方法的位置。否则不会有这样的替换导致硬错误。有关更深入的讨论,请参阅 this question。
假设我有以下 class:
class Extra final
{
public:
void Method1() const {std::cout << "Method 1" << std::endl;}
void Method2() const {std::cout << "Method 2" << std::endl;}
};
我为一些classT
创建了一个class模板,可以根据不同的场景在编译时使用Extra
enable/disable方法(在枚举中列出):
enum class Scenario
{
None, // No extra.
One, // Only Method1 allowed.
Two, // Only Method2 allowed.
Both // Both Method1 and Method2 allowed.
};
class 模板如下所示:
template<typename T, Scenario R>
class ExtraAdder : public T
{
};
template<typename T>
class ExtraAdder<T, Scenario::One> : public T
{
public:
void Method1Extra() const {m_extra.Method1();}
private:
Extra m_extra;
};
template<typename T>
class ExtraAdder<T, Scenario::Two> : public T
{
public:
void Method2Extra() const {m_extra.Method2();}
private:
Extra m_extra;
};
template<typename T>
class ExtraAdder<T, Scenario::Both> : public T
{
public:
void Method1Extra() const {m_extra.Method1();}
void Method2Extra() const {m_extra.Method2();}
private:
Extra m_extra;
};
可以如下使用:
class Base
{
public:
virtual ~Base() = default;
void BaseMethod() const {std::cout << "Base method" << std::endl;}
// Some other stuff...
};
int main(int argc, char **argv)
{
std::cout << "Scenario: None" << std::endl;
ExtraAdder<Base, Scenario::None> none;
none.BaseMethod();
//none.Method1Extra(); // Does not compile.
//none.Method2Extra(); // Does not compile.
std::cout << std::endl << "Scenario: One" << std::endl;
ExtraAdder<Base, Scenario::One> one;
one.BaseMethod();
one.Method1Extra();
//one.Method2Extra(); // Does not compile.
std::cout << std::endl << "Scenario: Two" << std::endl;
ExtraAdder<Base, Scenario::Two> two;
two.BaseMethod();
//two.Method1Extra(); // Does not compile.
two.Method2Extra();
std::cout << std::endl << "Scenario: Both" << std::endl;
ExtraAdder<Base, Scenario::Both> both;
both.BaseMethod();
both.Method1Extra();
both.Method2Extra();
return 0;
}
重点是,通过更改模板参数 Scenario
,我只能访问包装的 m_extra
成员的某些方法,这正是我想要的。然而,正如您所看到的,不同的专业非常冗长(并且有一些重复)。
问题:有没有一种方法可以产生完全相同的模板特化,但以一种不太冗长的方式,重复有限或没有重复?
我发现最接近解决这个问题的是 this question(使用 std::enable_if
),但我无法根据我的情况调整它。
我正在使用符合 C++17 标准的 g++。
您可以使用 enable_if
但您还需要制作成员函数模板函数以便检查可以依赖于某些东西:
template<typename T, Scenario R>
class ExtraAdder : public T {
Extra m_extra;
public:
template<bool B = true>
std::enable_if_t<B && (R == Scenario::One || R == Scenario::Both)>
Method1Extra() const {
m_extra.Method1();
}
template<bool B = true>
std::enable_if_t<B && (R == Scenario::Two || R == Scenario::Both)>
Method2Extra() const {
m_extra.Method2();
}
};
如果您不需要 SFINAE 了解这些方法是否存在,您可以简单地 static_assert
:
template<typename T, Scenario R>
class ExtraAdder : public T {
Extra m_extra;
public:
void Method1Extra() const {
static_assert(R == Scenario::One || R == Scenario::Both);
m_extra.Method1();
}
void Method2Extra() const {
static_assert(R == Scenario::Two || R == Scenario::Both);
m_extra.Method2();
}
};
或者,您可以将 enable_if
放入模板而不是 return 类型声明。我个人觉得这更具可读性,它的优点是可以使用 return 类型推导。
template<typename T, Scenario R>
class ExtraAdder : public T {
Extra m_extra;
public:
template<
Scenario S = R,
typename = std::enable_if_t<(S == Scenario::One || S == Scenario::Both)>
>
void Method1Extra() const {
m_extra.Method1();
}
template<
Scenario S = R,
typename = std::enable_if_t<(S == Scenario::Two || S == Scenario::Both)>
>
decltype(auto) Method2Extra() const {
return m_extra.Method2();
}
};
实例here.
将 enable_if
与成员函数一起使用的一个常见陷阱是,它仅在模板参数的替换格式不正确时才有效。因此,我们需要添加额外的模板参数 Scenario S = R
(或另一个答案中的 bool
),以便将替换推迟到使用该方法的位置。否则不会有这样的替换导致硬错误。有关更深入的讨论,请参阅 this question。