具有递归模板参数包函数是否为每次迭代创建特定函数?

时间:2016-04-14 01:19:46

标签: c++ variadic-templates compiler-optimization variadic-functions

我们假设我使用参数包重新创建printf

void printf(const char* string)
{
    std::cout << string;
}

template <typename T, typename... Params>
void printf(const char* string, T first, Params... p)
{
    while (*string != '\0')
    {
        if (*string == '%' && *(string + 1) != '%')
        {
                std::cout << first;
                string += 2;
                printf(string, p...);
                return;
        }

        std::cout << *string++;
    }
}

我使用以下参数调用该函数:

printf("%d is the answer to life, the universe, and everything. Pi = %f", 42, 3.14);

编译器是否会创建具有不同签名的2个不同函数?

printf(const char*, int, Params...);printf(const char*, double, Params...);

如果是这样,带有10个参数的函数调用将导致创建10个不同的函数。编译器是否对此进行了优化?

1 个答案:

答案 0 :(得分:4)

我希望通过此调用得到以下两个模板实例:

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Text;
        using System.Threading.Tasks;
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Data;
        using System.Windows.Documents;
        using System.Windows.Input;
        using System.Windows.Media;
        using System.Windows.Media.Imaging;
        using System.Windows.Navigation;
        using System.Windows.Shapes;
        using System.Windows.Markup;

        namespace Infinity.Shell.Controls.Docking
        {
            /// <summary>
            /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
            ///
            /// Step 1a) Using this custom control in a XAML file that exists in the current project.
            /// Add this XmlNamespace attribute to the root element of the markup file where it is 
            /// to be used:
            ///
            ///     xmlns:MyNamespace="clr-namespace:Infinity.Shell.Controls.Docking"
            ///
            ///
            /// Step 1b) Using this custom control in a XAML file that exists in a different project.
            /// Add this XmlNamespace attribute to the root element of the markup file where it is 
            /// to be used:
            ///
            ///     xmlns:MyNamespace="clr-namespace:Infinity.Shell.Controls.Docking;assembly=Infinity.Shell.Controls.Docking"
            ///
            /// You will also need to add a project reference from the project where the XAML file lives
            /// to this project and Rebuild to avoid compilation errors:
            ///
            ///     Right click on the target project in the Solution Explorer and
            ///     "Add Reference"->"Projects"->[Browse to and select this project]
            ///
            ///
            /// Step 2)
            /// Go ahead and use your control in the XAML file.
            ///
            ///     <MyNamespace:SplitContainer/>
            ///
            /// </summary>

            public class SplitContainer : Control
            {
                public Orientation Orientation
                {
                    get { return (Orientation)GetValue(OrientationProperty); }
                    set { SetValue(OrientationProperty, value); }
                }

                // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
                public static readonly DependencyProperty OrientationProperty =
                    DependencyProperty.Register("Orientation", typeof(Orientation), typeof(SplitContainer), new PropertyMetadata(Orientation.Horizontal));

                public UIElement FirstChild
                {
                    get { return (UIElement)GetValue(FirstChildProperty); }
                    set { SetValue(FirstChildProperty, value); }
                }

                // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
                public static readonly DependencyProperty FirstChildProperty =
                    DependencyProperty.Register("FirstChild", typeof(UIElement), typeof(SplitContainer), new PropertyMetadata(null));

                public UIElement SecondChild
                {
                    get { return (UIElement)GetValue(SecondChildProperty); }
                    set { SetValue(SecondChildProperty, value); }
                }

                // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
                public static readonly DependencyProperty SecondChildProperty =
                    DependencyProperty.Register("SecondChild", typeof(UIElement), typeof(SplitContainer), new PropertyMetadata(null));


                static SplitContainer()
                {
                    DefaultStyleKeyProperty.OverrideMetadata(typeof(SplitContainer), new FrameworkPropertyMetadata(typeof(SplitContainer)));
                }

                public override void OnApplyTemplate()
                {
                    base.OnApplyTemplate();
                }
            }
        }

        <Style TargetType="{x:Type dock:SplitContainer}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type dock:SplitContainer}">
                        <Border Background="{TemplateBinding Background}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}">
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition/>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition/>
                                </Grid.RowDefinitions>

                                <Grid.ColumnDefinitions> <!--Horizontal -->
                                    <ColumnDefinition/>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition/>
                                </Grid.ColumnDefinitions>

                                <Grid x:Name="PART_FirstChildGrid"
                                      Grid.Column="0"
                                      Grid.Row="0"
                                      Grid.RowSpan="3">
                                    <ContentPresenter Content="{TemplateBinding FirstChild}"/>
                                </Grid>

                                <GridSplitter x:Name="PART_Splitter"
                                              Grid.Column="1"
                                              Grid.Row="0"
                                              Grid.RowSpan="3"
                                              Grid.ColumnSpan="1"
                                              Width="5"
                                              VerticalAlignment="Stretch"
                                              HorizontalAlignment="Center"
                                              ShowsPreview="True"/>

                                <Grid x:Name="PART_SecondChildGrid"
                                      Grid.Column="2"
                                      Grid.Row="0"
                                      Grid.RowSpan="3">
                                    <ContentPresenter Content="{TemplateBinding SecondChild}"/>
                                </Grid>
                            </Grid>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="Orientation" Value="Vertical">
                                <Setter TargetName="PART_FirstChildGrid" Property="Grid.Column" Value="0"/>
                                <Setter TargetName="PART_FirstChildGrid" Property="Grid.Row" Value="0"/>
                                <Setter TargetName="PART_FirstChildGrid" Property="Grid.ColumnSpan" Value="3"/>
                                <Setter TargetName="PART_FirstChildGrid" Property="Grid.RowSpan" Value="1"/>

                                <Setter TargetName="PART_Splitter" Property="Grid.Column" Value="0"/>
                                <Setter TargetName="PART_Splitter" Property="Grid.Row" Value="1"/>
                                <Setter TargetName="PART_Splitter" Property="Grid.ColumnSpan" Value="3"/>
                                <Setter TargetName="PART_Splitter" Property="Grid.RowSpan" Value="1"/>
                                <Setter TargetName="PART_Splitter" Property="VerticalAlignment" Value="Center"/>
                                <Setter TargetName="PART_Splitter" Property="HorizontalAlignment" Value="Stretch"/>
                                <Setter TargetName="PART_Splitter" Property="Width" Value="Auto"/>
                                <Setter TargetName="PART_Splitter" Property="Height" Value="5"/>

                                <Setter TargetName="PART_SecondChildGrid" Property="Grid.Column" Value="0"/>
                                <Setter TargetName="PART_SecondChildGrid" Property="Grid.Row" Value="2"/>
                                <Setter TargetName="PART_SecondChildGrid" Property="Grid.ColumnSpan" Value="3"/>
                                <Setter TargetName="PART_SecondChildGrid" Property="Grid.RowSpan" Value="1"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

    <Window
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Infinity.Shell.Controls.Text;assembly=Infinity.Shell"
            xmlns:dock="clr-namespace:Infinity.Shell.Controls.Docking;assembly=Infinity.Shell"
x:Class="Infinity.MainWindow"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="499.573" Background="#FF1E1E1E">
        <Grid>
            <dock:SplitContainer Orientation="Vertical">
                <dock:SplitContainer.FirstChild>
                    <dock:SplitContainer>
                        <dock:SplitContainer.FirstChild>
                            <Button Content="First Child Button"/>
                        </dock:SplitContainer.FirstChild>
                        <dock:SplitContainer.SecondChild>
                            <Button Content="Second Child Button"/>
                        </dock:SplitContainer.SecondChild>
                    </dock:SplitContainer>
                </dock:SplitContainer.FirstChild>
                <dock:SplitContainer.SecondChild>
                    <Button Content="First Splitter Second Child"/>
                </dock:SplitContainer.SecondChild>
            </dock:SplitContainer>
        </Grid>


    </Window>

printf(const char*, int, double);

而且,是的,如果将11个参数传递给此printf(const char*, double); ,编译器将生成10个模板函数实例。这就是为什么模板因创建代码膨胀而享有声誉的原因。

如果有,请说

printf()

我希望两个模板实例都是相同的,并在链接时合并在一起。但这是一个小小的安慰。