简体   繁体   中英

Tagged dispatch with variadic templates, c++11

I'm trying to make a function accept different arguments depending on the enum.

// cake.h
#pragma once
#include <utility>

enum class TYPE { CupCake, Jelly, BirthdayCake };

struct cake_tin { /* etc etc */ };

/** Fills in different ingredients for different types of cake */
template <TYPE, typename ...Args>
void fill_tin(cake_tin &tin, Args... args);

/** Bakes a cake */
template <TYPE type, typename ...Args>
void bake_cake(Args&&...args) {
    cake_tin tin;
    fill_tin<type>(tin, std::forward<Args>(args)...);
}

.cpp file contains the specialisations of the function

// cake.cpp
#include "cake.h"

// If I put this in cake.h, I get multiple definition error (since the template's fully specalised), but if I move this to the .cpp file, I get undefined reference when I compile
template <>
void fill_tin<TYPE::CupCake>(cake_tin &tin, int cherries) {
    // ... etc etc
}

template <>
void fill_tin<TYPE::Jelly>(cake_tin &tin, bool wobble) {
    // ... etc etc
}

For completeness

// main.cpp
#include "cake.h"

int main() {
    bake_cake<TYPE::CupCake>(1);
}

Another file that includes cake.h

// other.cpp
#include "cake.h"

Compile with clang++ -Wall -Wextra -std=c++11 main.cpp other.cpp -o main

I've tried putting in declarations

template <> void fill_tin<TYPE::CupCake>(cake_tin &tin, int cherries);
template <> void fill_tin<TYPE::Jelly>(cake_tin &tin, bool wobble);

and still can't get that template instantiated. If I move the specialisations into the header then they'll cause redefinition when the header is included in multiple compilation units at the link stage.

My question is: How can I get this to work, or is there a better way of doing this?

Trying to mix template specialization with function overloading is always going to be horribly painful.

Either provide class template specializations:

template<TYPE> struct tin_filler;  // undefined
template<> struct tin_filler<TYPE::CupCake> {
    void operator()(cake_tin& tin, int cherries); };
template<> struct tin_filler<TYPE::Jelly> {
    void operator()(cake_tin& tin, bool wobble); };

template <TYPE type, typename ...Args>
void bake_cake(Args&&...args) {
    cake_tin tin;
    tin_filler<type>()(tin, std::forward<Args>(args)...);
}

Or use integral_constant to reify the TYPE and use normal function overloading:

void fill_tin(std::integral_constant<TYPE, TYPE::CupCake>, cake_tin& tin, int cherries);
void fill_tin(std::integral_constant<TYPE, TYPE::Jelly>, cake_tin& tin, bool wobble);

template <TYPE type, typename ...Args>
void bake_cake(Args&&...args) {
    cake_tin tin;
    fill_tin(std::integral_constant<TYPE, type>(), tin, std::forward<Args>(args)...);
}

The former is more general, but the latter could well be clearer and simpler in this case.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM