จะตรวจสอบประเภทของพารามิเตอร์เทมเพลตได้อย่างไร?


97

สมมติว่าฉันมีฟังก์ชันเทมเพลตและสองคลาส

class animal {
}
class person {
}

template<class T>
void foo() {
  if (T is animal) {
    kill();
  }
}

ฉันจะตรวจสอบ T คือสัตว์ได้อย่างไร? ฉันไม่ต้องการให้มีบางอย่างที่ตรวจสอบระหว่างเวลาทำงาน ขอบคุณ


62
ฉันจะใส่ "สัตว์เลี้ยง" แทน "ฆ่า" :-)
JimBamFeng

คำตอบ:


135

ใช้is_same:

#include <type_traits>

template <typename T>
void foo()
{
    if (std::is_same<T, animal>::value) { /* ... */ }  // optimizable...
}

โดยปกติแล้วนั่นเป็นการออกแบบที่ไม่สามารถใช้งานได้โดยสิ้นเชิงและคุณต้องการที่จะเชี่ยวชาญ :

template <typename T> void foo() { /* generic implementation  */ }

template <> void foo<animal>()   { /* specific for T = animal */ }

โปรดทราบว่าเป็นเรื่องผิดปกติที่จะมีเทมเพลตฟังก์ชันที่มีอาร์กิวเมนต์ที่ชัดเจน (ไม่อนุมาน) ไม่ใช่เรื่องแปลก แต่มักจะมีแนวทางที่ดีกว่า


2
ขอบคุณ! จริงๆแล้วพวกเขาแบ่งปันรหัสจำนวนมากดังนั้นฉันจึงไม่สามารถทำซ้ำได้จริง ๆ
WhatABeautifulWorld

3
@WhatABeautifulWorld: คุณสามารถแยกตัวประกอบโค้ดของคุณได้ตลอดเวลาเพื่อให้ส่วนที่ขึ้นอยู่กับประเภทสามารถลดลงเป็นฟังก์ชันพิเศษได้ ...
Kerrek SB

1
การติดตามผลอย่างรวดเร็วเพียงครั้งเดียวหากฉันใช้ std :: is_same มันจะไม่ทำให้โค้ดช้าลงสำหรับพารามิเตอร์เทมเพลตอื่น ๆ ใช่ไหม
WhatABeautifulWorld

1
@WhatABeautifulWorld: ค่าลักษณะเป็นที่รู้จักกันแบบคงที่ ไม่ควรมีค่ารันไทม์ใด ๆ หากคอมไพเลอร์ของคุณดีเพียงครึ่งเดียว ตรวจสอบการประกอบหากมีข้อสงสัย
Kerrek SB

2
@ AdriC.S: เนื่องจากTไม่ได้อนุมานคุณจึงทำอะไรได้ไม่มาก คุณสามารถออกจากแม่แบบหลัก unimplemented is_sameและสร้างความเชี่ยวชาญหรือคุณอาจจะเพิ่มการยืนยันคงที่พร้อม
Kerrek SB

38

ฉันคิดว่าวันนี้มันจะดีกว่าที่จะใช้ แต่เฉพาะกับ C ++ 17

#include <type_traits>

template <typename T>
void foo() {
    if constexpr (std::is_same_v<T, animal>) {
        // use type specific operations... 
    } 
}

หากคุณใช้การดำเนินการเฉพาะบางประเภทใน if expression body โดยไม่มีconstexprโค้ดนี้จะไม่คอมไพล์


8
แทนที่จะstd::is_same<T, U>::valueใช้สั้นกว่า:std::is_same_v<T, U>
Fureeish

12

ใน C ++ 17 เราสามารถใช้ตัวแปรสายพันธุ์

ในการใช้งานstd::variantคุณต้องรวมส่วนหัว:

#include <variant>

หลังจากนั้นคุณสามารถเพิ่มstd::variantโค้ดของคุณได้ดังนี้:

using Type = std::variant<Animal, Person>;

template <class T>
void foo(Type type) {
    if (std::is_same_v<type, Animal>) {
        // Do stuff...
    } else {
        // Do stuff...
    }
}

8
T และ Type เชื่อมต่อกันอย่างไร?
mabraham

4
คำตอบนี้มีปัญหาหลายประการ นอกจากนี้ข้อผิดพลาดที่เกิดขึ้นจริง ( typeซึ่งเป็นค่าประเภทTypeหรือแม่แบบซึ่งไม่ได้ทำให้ความรู้สึกที่นี่) ไม่ได้มีความหมายในบริบทของis_same_v variantที่สอดคล้องกัน "ลักษณะ" holds_alternativeเป็น
Pixelchemist

std::variantไม่จำเป็นโดยสิ้นเชิงที่นี่
tjysdsg

7

คุณสามารถเชี่ยวชาญเทมเพลตของคุณโดยพิจารณาจากสิ่งที่ส่งไปยังพารามิเตอร์เช่นนี้:

template <> void foo<animal> {

}

Tโปรดทราบว่านี้จะสร้างฟังก์ชั่นใหม่ทั้งหมดขึ้นอยู่กับชนิดที่ผ่านการเป็น โดยปกติแล้วจะดีกว่าเนื่องจากช่วยลดความยุ่งเหยิงและเป็นเหตุผลหลักที่เรามีเทมเพลตตั้งแต่แรก


อืม. วิธีนี้เป็นวิธีเดียวที่ดีกว่าในการจัดการอาร์กิวเมนต์แม่แบบหรือไม่? สมมติว่าฉันมีคลาสย่อย 10 คลาสที่ฉันต้องจัดการภายในฟังก์ชันเทมเพลต ฉันต้องเขียนฟังก์ชันเทมเพลต 10 แบบสำหรับคลาสที่เกี่ยวข้องจริง ๆ หรือไม่? ฉันคิดว่าฉันอาจจะพลาดประเด็นหลักที่นี่
Volkan Güven

นี่เป็นความคิดที่ดีจริงๆหากมีคนไม่ต้องการใช้ type_traits เช่นเดียวกับที่มีคนกล่าวถึงตรรกะหลักสามารถทำได้ในฟังก์ชันอื่นซึ่งยอมรับการตั้งค่าสถานะพิเศษเพื่อระบุประเภทและการประกาศพิเศษนี้สามารถตั้งค่าสถานะตามนั้นและส่งต่อข้อโต้แย้งอื่น ๆ ทั้งหมดโดยตรงโดยไม่ต้องแตะต้องอะไรเลย ดังนั้นหากต้องจัดการ 10 คลาสที่แตกต่างกันโดยทั่วไปจะเป็น 10 บรรทัดสำหรับนิยามฟังก์ชันที่แตกต่างกัน 10 รายการ แต่สิ่งนี้จะซับซ้อนมากหากมีตัวแปรเทมเพลตมากกว่า 1 ตัว
Harish Ganesan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.