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

时间:2017-07-01 19:19:58

标签: c++ c++11 templates

我有以下代码:

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'

非常感谢任何帮助。

4 个答案:

答案 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上查看。
如果您还想初始化对象,可以执行以下操作(只要其聚合类型为AB):

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>{};
  }
}

either implementation

如果条件是编译时间

typename std::conditional</* condition */, C<A>, C<B>>::type();