根据条件从函数模板返回不同的类型

Avi*_*Avi 3 c++ templates c++11

我有以下代码:

helper.hpp:

struct A {
   uint32_t a, b;
};

struct B {
  uint32_t a, b;
};

template <typename T>
struct C {
  T barcode;
};
Run Code Online (Sandbox Code Playgroud)

现在基于某些条件我想在main.cpp中创建适当的struct对象

if(/* something */) {
  C<A> obj;
}
else {
  C<B> obj;
}
Run Code Online (Sandbox Code Playgroud)

现在问题是因为它在if范围内我无法访问它.处理它的一种方法是从函数返回对象,如下所示:

template <typename T> 
C<T> getObject(){
  if(/* something */) {
    return C<A>{};
  }
  else{
    return C<B>{};
  }
}

auto obj = getObject()
Run Code Online (Sandbox Code Playgroud)

但这给了我以下编译错误:

错误:没有用于调用'getObject()注释的匹配函数:无法推导出模板参数'T'

真的很感激任何帮助.

Gui*_*cot 7

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>();
Run Code Online (Sandbox Code Playgroud)

如您所见,else中的代码没有意义.您不能C<B>在必须返回的函数内返回类型的值C<A>.作为代码的编译时间实例的副作用,C<A>并且C<B>是无关的并且是完全不同的类型.

此外,您可以看到auto已被替换为C<A>.这是因为auto在编译时也是推断的.


现在......您可以做些什么来使您的代码工作?

有多个解决方案和抽象来拥有一个具有运行时定义类型的变量.我将介绍一些您可以使用的选项.

使用变体

变量是一个类,它可以保存变量的单个实例,该变量可以是不同类型,在有限的类型列表中指定.例如,a 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>>
Run Code Online (Sandbox Code Playgroud)

如果您无法访问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>
Run Code Online (Sandbox Code Playgroud)

请注意,如果这是您要执行的操作,则必须在其中定义一些通用接口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其他形式的类型擦除技术可用.这个答案已经很长了.您可以在线免费找到大量文档,深入介绍所有这些内容.


Rak*_*111 5

我仍然想知道是否有任何有用的情况,你会想要这样的东西......但我能想到的唯一方法是何时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>{};
}
Run Code Online (Sandbox Code Playgroud)

用法非常简单:

auto obj = getObject();
Run Code Online (Sandbox Code Playgroud)

  • @Avi `if constexpr` 也仅在 C++17 中可用:) 好吧,也许我会找到其他东西.. (2认同)