我有以下代码:
helper.hpp :
struct A {
uint32_t a, b;
};
struct B {
uint32_t a, b;
};
template <typename T>
struct C {
T barcode;
};
现在基于某些条件我想在main.cpp中创建适当的struct对象
if(/* something */) {
C<A> obj;
}
else {
C<B> obj;
}
现在的问题是,因为它在if范围内,我无法访问它。 处理它的一种方法是从函数返回对象,如下所示:
template <typename T>
C<T> getObject(){
if(/* something */) {
return C<A>{};
}
else{
return C<B>{};
}
}
auto obj = getObject()
但是这给了我以下编译错误:
错误:没有用于调用'getObject()的匹配函数 注意:无法推断模板参数'T'
非常感谢任何帮助。
答案 0 :(得分:6)
C ++中的类型是在编译时确定的。这意味着运行时条件不能用于推断对象的类型。
看到你试图使用模板的方式告诉我,这里有一点误解。模板代码由编译器实例化。模板中的代码在实例化之前不是真正的代码。如果我们手动使用类型A
进行代码实例化,它会看起来像这样(非实际代码):
template <>
auto getObject<A>() -> C<A> {
if(/* something at runtime */) {
return C<A>{};
} else {
return C<B>{};
}
}
// auto
C<A> obj = getObject<A>();
正如您所看到的,else中的代码没有意义。您不能在必须返回C<B>
的函数内返回类型C<A>
的值。作为代码编译时实例化的副作用,C<A>
和C<B>
是不相关的,并且是完全不同的类型。
此外,您可以看到auto
已被C<A>
取代。这是因为在编译时也推断出auto
。
现在......您可以做些什么来使您的代码有效?
有多个解决方案和抽象来拥有一个具有运行时定义类型的变量。我将介绍一些您可以使用的选项。
变量是一个类,它可以包含一个变量的单个实例,该变量可以是不同类型的,在有限的类型列表中指定。例如,std::variant<int, std::string>
是一个变量,可以是整数或字符串。
在您的代码中,它会是C<A>
和C<B>
的变体:
auto getObject() -> std::variant<C<A>, C<B>> {
if (/* something at runtime */) {
return C<A>{};
} else {
return C<B>{};
}
}
auto obj = getObject();
// The type of obj is std::variant<C<A>, C<B>>
如果您无法访问C ++ 17,则可以始终使用boost::variant
。
此解决方案的缺点是您必须了解变体可以采用的每种类型。如果您有无限数量的类型,则不能使用变体。然而,它非常快并且促进规律性(价值语义)。
虚拟多态是获取在运行时决定的不同类型变量的最常用方法。它看起来不错,但是它带有指针和动态分配的价格,并且相当具有侵入性。它看起来像这样:
struct CC {
virtual ~CC() = default;
};
template<typename T>
struct C : CC {
T barcode;
};
auto getObject() -> std::unique_ptr<CC> {
if (/* something at runtime */) {
return std::make_unique<C<A>>();
} else {
return std::make_unique<C<B>>();
}
}
auto obj = getObject();
// The type of obj is std::unique_ptr<CC>
请注意,如果您要执行此操作,则必须在CC
中定义一些通用接口。其余的代码将使用该通用接口,以便对C
及其条形码进行操作。
请注意std::make_unique
是C ++ 14的一部分。它可以分别由std::unique_ptr<C<A>>{new C<A>}
和std::unique_ptr<C<B>>{new C<B>}
替换。
还有std::any
和其他形式的类型擦除技术可用。这个答案已经很长了。您可以在线免费找到大量文档,深入介绍所有这些文档。
答案 1 :(得分:2)
我仍然想知道是否有任何有用的案例,你会想要这样的东西......但我能想到的唯一方法是在编译时可以评估condition
,因为将使编译器能够使用这两种类型中的任何一种。
// 'auto' to let the compiler deduce the correct type at compile time.
auto getObject() {
// those 2 branches are evaluated at compile time only, which lets the compiler
// discard the other branch, making the code valid.
if constexpr(/*something at compile time*/)
return C<A>{};
else
return C<B>{};
}
用法非常简单:
auto obj = getObject();
答案 2 :(得分:1)
你可以这样做:
template <typename T>
C<T> getObject(){
return {};
}
在wandbox上查看。
如果您还想初始化对象,可以执行以下操作(只要其聚合类型为A
或B
):
template <typename T, typename... A>
C<T> getObject(A&&... args){
return { std::forward<A>(args)... };
}
答案 3 :(得分:1)
如果条件是运行时
要解决无关返回类型的问题,您只需以这种方式修改代码:
either<C<A>, C<B>> getObject() {
if(/* something */){
return C<A>{};
}
else{
return C<B>{};
}
}
如果条件是编译时间
typename std::conditional</* condition */, C<A>, C<B>>::type();