C ++函数模板问题

Jon*_*ood 3 c++ templates

我使用C#已有很长时间了,我对C ++中的函数模板有一些疑问。

template <typename T>
T max(T x, T y)
{
    return (x > y) ? x : y;
}
Run Code Online (Sandbox Code Playgroud)
  1. 为什么在模板参数声明中typename使用某些示例而使用其他示例class?有什么不同?
  2. 是否有任何方法可以限制T到特定类型,或从特定类型派生的类型?
  3. 一个类是否可以通过两种方法使用相同的名称,但一种方法是模板化的,而另一种则不是?

更新:

我很感谢所有答案,但是其中一些包含了一些示例,当我尝试将其应用于我的代码时,这些示例将不会编译。

为了澄清问题3,我有以下方法:

template<typename T>
std::unique_ptr<T> ExecuteSqlQuery(LPCTSTR pszSqlQuery, UINT nOpenType = AFX_DB_USE_DEFAULT_TYPE);
Run Code Online (Sandbox Code Playgroud)

我想声明使用CRecordsetas 的此变量的变体T,以使以下任一语句均有效:

auto result = db.ExecuteSqlQuery<CCustomerRecordset>(L"SELECT ...");

auto result = db.ExecuteSqlQuery(L"SELECT ...");
Run Code Online (Sandbox Code Playgroud)

wal*_*nut 9

为什么在模板参数声明中typename使用某些示例而使用其他示例class?有什么不同?

模板参数声明中的两者没有什么区别,但是在其他情况下,它们都有其他单独的含义。例如typename,用于将从属名称标记为类型名称,class并用于引入类声明。

有什么方法可以将T限制为特定类型,或从特定类型派生的类型?

是的,一种方法是依靠SFINAE放弃满足某些条件的类型的实例化,通常由来促进std::enable_if,例如(使用C ++ 14):

template<typename T, typename = std::enable_if_t<std::is_base_of_v<SomeBaseClass, T>>
T max(T x, T y)
{
    return (x > y) ? x : y;
}
Run Code Online (Sandbox Code Playgroud)

在即将发布的C ++ 20中,将支持Concepts,使您可以编写

template<std::DerivedFrom<SomeBaseClass> T>
T max(T x, T y)
{
    return (x > y) ? x : y;
}
Run Code Online (Sandbox Code Playgroud)

一个类是否可以通过两种方法使用相同的名称,但一种方法是模板化的,而另一种则不是?

是的,这是可能的。在过载解决方案中,如果两个候选者都具有相同的匹配性,则首选非模板化者。


Igo*_* R. 6

  1. 在这种特定情况下,两者classtypename均值相同,没有区别。class只是短一点:-)。
  2. 在C ++ 20之前,我们可以结合SFINAE技术尝试使用复杂的模板元编程来限制模板参数。基本上,如果参数不满足某些条件,它将使模板实例化失败。尽管这是一种非常强大的方法,但它也有缺点:增加了编译时间,并且错误信息非常长且不清楚。

    在C ++ 20中,我们有一个名为概念的新语言功能,旨在以简单明了的方式实现完全相同的功能。

  3. 是的,功能模板可以使用常规功能进行重载。如果两者都匹配,则将选择常规功能。但是请注意,一般而言,模板重载解析是一个非常复杂的主题。


rob*_*oke 5

  1. 老派的C ++使用了“类”,但是现在我们使用了“类型名”。您仍然可以使用类,但建议使用typename。
  2. 是的,您可以通过专业化来限制类型。
  template<typename T> T foo(T x); //< no implementation in the generic case

  template<> T foo<float>(T x) { return x; } //< float is allowed
  template<> T foo<double>(T x) { return x; } //< double is allowed
Run Code Online (Sandbox Code Playgroud)

您也可以处理派生类型(有几种方法可以做到这一点)

#include <string>
#include <iostream>

struct Cow {};

template<typename T>
struct Moo
{
  // default to false
  template<bool valid = std::is_base_of<Cow, T>::value>
  static void moo()
  {
    std::cout << "No moo for you!" << std::endl;
  }

  // moo if T is a cow
  template<>
  static void moo<true>()
  {
    std::cout << "Mooooo!" << std::endl;
  }
};

struct AberdeenAngus : public Cow {};
struct Sheep {};

int main()
{
    Moo<AberdeenAngus>::moo();
    Moo<Sheep>::moo();
    return 0;
}

Run Code Online (Sandbox Code Playgroud)
  1. 是。
class Foo
{
public:
  template<typename T>
  T thing(T a) { return a; } //< template

  float thing(float a) { return a * 5.0f; } //< function overload
};
Run Code Online (Sandbox Code Playgroud)


Jar*_*d42 5

为什么在模板参数声明中有些示例使用typename而其他示例使用class?有什么不同?

从历史上看,

typename允许用于简单模板,并且class应该用于模板模板参数:

template <template <typename> class C> void foo();
Run Code Online (Sandbox Code Playgroud)

使用如

foo<std::unique_ptr>();
Run Code Online (Sandbox Code Playgroud)

现在 (C++17) 在这些上下文中是可互换的。

有没有办法限制T到特定类型,或限制到从特定类型派生的类型?

你可以用SFINAE(它有几种语法)来做到这一点,在 C++20 中用Concepts来做到这一点。

template <typename T>
std::enable_if_t<some_trait<T>::value> foo();
Run Code Online (Sandbox Code Playgroud)

有没有办法让一个类有两个同名的方法,除了一个是模板化的,另一个不是?

是的,你可能有几个这样的重载

template <template <class> typename C> void foo();
template <int> void foo();
void foo();
Run Code Online (Sandbox Code Playgroud)

或者更简单

template <typename T> void foo(T); // #1
void foo(int); // #2

// Note that foo<int> (#1 with T = int) is different than foo (#2)
Run Code Online (Sandbox Code Playgroud)