4

I've been using C# so long, I have a couple of questions about function templates in C++.

template <typename T>
T max(T x, T y)
{
    return (x > y) ? x : y;
}
  1. Why do some examples use typename and other examples use class in the template parameter declaration? What is the difference?
  2. Is there any way to restrict T to a particular type, or to a type that derives from a particular type?
  3. Is there any way for a class to have two methods with the same name, except one is templated and the other is not?

UPDATE:

I appreciate all the answers, but several of them contain examples that I won't compile when I try to apply them to my code.

To clarify question 3, I have the following method:

template<typename T>
std::unique_ptr<T> ExecuteSqlQuery(LPCTSTR pszSqlQuery, UINT nOpenType = AFX_DB_USE_DEFAULT_TYPE);

I would like to declare a variation of this that uses CRecordset as T, so that either of the following statements would be valid:

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

auto result = db.ExecuteSqlQuery(L"SELECT ...");
Jonathan Wood
  • 65,341
  • 71
  • 269
  • 466
  • More details about the first question [Difference of keywords 'typename' and 'class' in templates](https://stackoverflow.com/questions/2023977/difference-of-keywords-typename-and-class-in-templates) – Lion King Aug 23 '19 at 04:01

4 Answers4

9

Why do some examples use typename and other examples use class in the template parameter declaration? What is the difference?

There is no difference between the two in the template parameter declaration, however they both have additional separate meanings in other contexts. E.g. typename is used to mark dependent names as type names and class is used to introduce a class declaration.

Is there any way to restrict T to a particular type, or a type that derives from a particular type?

Yes, one way is to rely on SFINAE to discard instantiations of types satisfying some condition, often facilitated by std::enable_if, e.g. (using 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;
}

In the upcoming C++20, there will be support for Concepts, which allow one to write

template<std::DerivedFrom<SomeBaseClass> T>
T max(T x, T y)
{
    return (x > y) ? x : y;
}

Is there any way for a class to have two methods with the same name, except one is templated and the other is not?

Yes, this is possible. In overload resolution, if both candidates would be equally well matching, the non-templated one will be preferred.

walnut
  • 21,629
  • 4
  • 23
  • 59
6
  1. In this particular context both class and typename mean exaclty the same, there is no difference. class is just a bit shorter :-).

  2. Until C++20 we could try and restrict template arguments using sophisticated template metaprogramming in conjunction with SFINAE technique. Basically, it makes template instantiation fail if the argument does not satisfy some condition. While it's very powerfull approach, it has its drawbacks: increased compile times and very long and unclear error messages.

    In C++20 we have a new language feature named concepts, which is aimed to do exactly the same in a simple and straightforward way.

  3. Yes, a function template can be overloaded with a regular function. If the both match, the regular function will be chosen. Note however that in general template overload resolution is quite complicated topic.

Community
  • 1
  • 1
Igor R.
  • 14,716
  • 2
  • 49
  • 83
5
  1. Old school C++ used 'class', but we now use 'typename'. You can still use class, but typename is recommended.
  2. Yes, you can restrict types via specialisation..
  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

And you can handle derived types as well (and there are a few ways to do this)

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

  1. Yes.
class Foo
{
public:
  template<typename T>
  T thing(T a) { return a; } //< template

  float thing(float a) { return a * 5.0f; } //< function overload
};
robthebloke
  • 9,331
  • 9
  • 12
5

Why do some examples use typename and other examples use class in the template parameter declaration? What is the difference?

Historically,

Only typename was allowed for simple template, and class should be used for template template parameter:

template <template <typename> class C> void foo();

with usage such as

foo<std::unique_ptr>();

There are now (C++17) interchangeable in those contexts.

Is there any way to restrict T to a particular type, or to a type that derives from a particular type?

You might do that with SFINAE (which has several syntaxes), and in C++20 with Concepts.

template <typename T>
std::enable_if_t<some_trait<T>::value> foo();

Is there any way for a class to have two methods with the same name, except one is templated and the other is not?

Yes you might have several overloads that way

template <template <class> typename C> void foo();
template <int> void foo();
void foo();

or more simply

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

// Note that foo<int> (#1 with T = int) is different than foo (#2)
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Thanks, but I can't seem to get any variations of the last answer to work in my case. My method is `template std::unique_ptr ExecuteSqlQuery(LPCTSTR pszSqlQuery, UINT nOpenType = AFX_DB_USE_DEFAULT_TYPE);`, and I'd like a version that uses `CRecordset` as `T` and doesn't require the caller to provide a type argument. I may need to ask a new question here. – Jonathan Wood Aug 23 '19 at 12:07
  • @JonathanWood: Something like [that](http://coliru.stacked-crooked.com/a/d9d305987e7b8350)? – Jarod42 Aug 23 '19 at 12:15
  • Interesting. That does, in fact, compile when I modified it as follows: `std::unique_ptr ExecuteSqlQuery(LPCTSTR pszSqlQuery, UINT nOpenType = AFX_DB_USE_DEFAULT_TYPE) { return ExecuteSqlQuery(pszSqlQuery, nOpenType); }`. However, I'm now getting an unresolved symbol by the linker: error LNK2019: unresolved external symbol "public: class std::unique_ptr > __thiscall CDatabaseCommon::ExecuteSqlQuery(wchar_t const *,unsigned int)" – Jonathan Wood Aug 23 '19 at 12:22
  • And, in fact, it compiles without the second method if I use your `template ` syntax. That is far cleaner if it does what I think it does. But I still get the linker errors. – Jonathan Wood Aug 23 '19 at 12:28
  • Looks like the entire function must be declared in the header file. – Jonathan Wood Aug 23 '19 at 12:54
  • @JonathanWood: As mostly every template. – Jarod42 Aug 23 '19 at 13:05