พารามิเตอร์เทมเพลตที่ไม่ใช่ประเภท


93

ฉันเข้าใจว่าพารามิเตอร์เทมเพลตที่ไม่ใช่ประเภทควรเป็นนิพจน์อินทิกรัลคงที่ มีใครช่วยส่องทำไมถึงเป็นอย่างนั้น?

template <std::string temp>
void foo()
{
     // ...
}
error C2993: 'std::string' : illegal type for non-type template parameter 'temp'.

ฉันเข้าใจว่านิพจน์ปริพันธ์คงที่คืออะไร อะไรคือสาเหตุที่ไม่อนุญาตประเภทที่ไม่คงที่เช่นstd::stringในตัวอย่างข้างต้น


17
พารามิเตอร์เทมเพลตได้รับการแก้ไขในเวลาคอมไพล์
Etienne de Martel

คำตอบ:


121

เหตุผลที่คุณไม่สามารถทำได้เนื่องจากนิพจน์ที่ไม่ใช่ค่าคงที่ไม่สามารถแยกวิเคราะห์และแทนที่ในระหว่างเวลาคอมไพล์ สามารถเปลี่ยนแปลงได้ในระหว่างรันไทม์ซึ่งจะต้องมีการสร้างเทมเพลตใหม่ระหว่างรันไทม์ซึ่งเป็นไปไม่ได้เนื่องจากเทมเพลตเป็นแนวคิดเกี่ยวกับเวลาคอมไพล์

นี่คือสิ่งที่มาตรฐานอนุญาตสำหรับพารามิเตอร์เทมเพลตที่ไม่ใช่ประเภท (14.1 [temp.param] p4):

เทมเพลตพารามิเตอร์ที่ไม่ใช่ประเภทต้องมีหนึ่งในประเภทต่อไปนี้ (เป็นตัวเลือกที่ผ่านการรับรอง cv):

  • ประเภทปริพันธ์หรือการแจงนับ
  • ตัวชี้ไปที่วัตถุหรือตัวชี้ให้ทำงาน
  • lvalue อ้างอิงถึง object หรือ lvalue อ้างอิงถึง function,
  • ชี้ไปที่สมาชิก
  • std::nullptr_t.

6
@ALOToverflow: อยู่ภายใต้ "ตัวชี้ไปที่สมาชิก" เป็น "ตัวชี้ไปยังฟังก์ชันสมาชิก" หรือ "ตัวชี้ไปยังข้อมูลสมาชิก"
Xeo

4
เป็นที่น่าสังเกตว่าในกรณีของพอยน์เตอร์ไปยังอ็อบเจ็กต์ (หรือฟิลด์อินสแตนซ์) อ็อบเจ็กต์ต้องมีระยะเวลาการจัดเก็บแบบคงที่และการเชื่อมโยง (ภายนอกก่อน C ++ 11 ภายในหรือภายนอกใน C ++ 11) เพื่อให้ตัวชี้ไปยังวัตถุเหล่านั้นได้ สร้างขึ้นในเวลาคอมไพล์
Theodore Murdock

2
ใน C ++ 20 ตอนนี้อนุญาตให้ใช้งานได้แล้วหากประเภทมีความเท่าเทียมกันเชิงโครงสร้างที่แข็งแกร่งเป็นวัตถุย่อยที่ไม่แปรผัน / ระเหยได้และที่ซึ่งตัวดำเนินการยานอวกาศเป็นสาธารณะ
Rakete1111

74

ที่ไม่ได้รับอนุญาต

อย่างไรก็ตามสิ่งนี้ได้รับอนุญาต:

template <std::string * temp> //pointer to object
void f();

template <std::string & temp> //reference to object
void g();

ดู§14.1 / 6,7,8 ใน C ++ Standard (2003)


ภาพประกอบ:

template <std::string * temp> //pointer to object
void f()
{
   cout << *temp << endl;
}

template <std::string & temp> //reference to object
void g()
{
     cout << temp << endl;
     temp += "...appended some string";
}

std::string s; //must not be local as it must have external linkage!

int main() {
        s = "can assign values locally";
        f<&s>();
        g<s>();
        cout << s << endl;
        return 0;
}

เอาท์พุต:

can assign values locally
can assign values locally
can assign values locally...appended some string

7
@ Mahesh: เนื่องจากสิ่งที่แม่แบบโดยทั่วไปใช้คือที่อยู่ของstd::stringตัวชี้หรือวัตถุอ้างอิงนั้น หากตัวแปรนั้นเป็นแบบโลคัลคุณอาจได้รับแอดเดรสที่แตกต่างกันทุกครั้งที่เรียกใช้ฟังก์ชัน
Xeo

11
@ Mahesh: คุณไม่รู้ว่า call-stack มีลักษณะอย่างไรในเวลาคอมไพล์ ก่อนฟังก์ชันของคุณอาจมีการเรียกอีก 10 รายการหรือ 3 รายการหรืออื่น ๆ อีกมากมายดังนั้นที่อยู่บนสแต็กที่สร้างสตริงจึงสามารถเปลี่ยนจากการโทรเป็นการโทรได้ เมื่อคุณมีอ็อบเจ็กต์ที่มีการเชื่อมโยงภายนอกแอดเดรสของอ็อบเจ็กต์จะถูกแก้ไขระหว่างการคอมไพล์ / ลิงก์
Xeo

2
@Xeo "ที่อยู่ของมันได้รับการแก้ไขระหว่างการคอมไพล์ / การเชื่อมโยง " หรือไม่สำหรับโค้ดที่ย้ายตำแหน่งได้หรือไม่ขึ้นกับตำแหน่ง
ซอกแซก

1
คำตอบนี้ (ในปัจจุบัน) ดูเหมือนจะไม่ตรงกับคำถามของ OP ซึ่งถามว่าเหตุใดจึงมีพฤติกรรมนี้ คำตอบนี้เป็นเพียงตัวอย่างของ OP โดยไม่ต้องให้คำอธิบายใด ๆ
Quuxplusone

1
ฉันมาสายสำหรับงานปาร์ตี้ดูเหมือนว่าตัวชี้ที่ชาญฉลาดจะไม่ทำงาน
Nicholas Humphrey

28

คุณต้องสามารถจัดการข้อโต้แย้งของเทมเพลตได้

template <std::string temp>
void f() {
 // ...
}

f<"foo">();
f<"bar">(); // different function!?

ตอนนี้นัยจะต้องสร้างลำดับอักขระที่ไม่ซ้ำกันสำหรับ a std::stringหรือสำหรับเรื่องนั้นคลาสอื่น ๆ ที่ผู้ใช้กำหนดโดยพลการจัดเก็บค่าเฉพาะซึ่งไม่ทราบความหมายของการใช้งาน และนอกจากนี้ค่าของอ็อบเจ็กต์คลาสที่กำหนดเองไม่สามารถคำนวณได้ในเวลาคอมไพล์

มีการวางแผนที่จะพิจารณาอนุญาตประเภทคลาสตามตัวอักษรเป็นประเภทพารามิเตอร์เทมเพลตสำหรับหลัง C ++ 0x ซึ่งเริ่มต้นโดยนิพจน์คงที่ สิ่งเหล่านี้อาจถูกทำให้ยุ่งเหยิงได้โดยให้สมาชิกข้อมูลวนซ้ำตามค่าของมัน (สำหรับคลาสพื้นฐานเช่นเราสามารถใช้ความลึกก่อนการส่งผ่านจากซ้ายไปขวา) แต่แน่นอนว่าจะไม่ได้ผลสำหรับชั้นเรียนโดยพลการ


10

อาร์กิวเมนต์แม่แบบที่ไม่ใช่ประเภทที่อยู่ในรายการอาร์กิวเมนต์แม่แบบคือนิพจน์ที่สามารถกำหนดค่าได้ในเวลาคอมไพล์ ข้อโต้แย้งดังกล่าวต้องเป็น:

นิพจน์คงที่อยู่ของฟังก์ชันหรืออ็อบเจ็กต์ที่มีการเชื่อมโยงภายนอกหรือแอดเดรสของสมาชิกคลาสแบบคงที่

นอกจากนี้สตริงลิเทอรัลยังเป็นอ็อบเจ็กต์ที่มีการเชื่อมโยงภายในดังนั้นคุณจึงไม่สามารถใช้เป็นอาร์กิวเมนต์ของเทมเพลตได้ คุณไม่สามารถใช้ตัวชี้ส่วนกลางได้เช่นกัน ไม่อนุญาตให้ใช้ตัวอักษรทศนิยมเนื่องจากมีความเป็นไปได้ที่ชัดเจนในการปัดเศษข้อผิดพลาด


คุณใช้ใบเสนอราคาแล้วที่มาคืออะไร? ฉันต้องการเพิ่มคะแนนหากมีแหล่งที่มาของการเสนอราคา
Gabriel Staples
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.